Skip to content
This repository has been archived by the owner on May 25, 2021. It is now read-only.

Commit

Permalink
Merge branch 'master' into search-crn
Browse files Browse the repository at this point in the history
  • Loading branch information
Bad-Science authored Apr 10, 2019
2 parents b1a4b49 + 4824f3b commit ca0d728
Show file tree
Hide file tree
Showing 27 changed files with 450 additions and 67 deletions.
2 changes: 1 addition & 1 deletion bin/yacs-purge-development
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ bin/yacs-compose-development stop kafka zookeeper redis core-consumer
bin/yacs-compose-development rm -vf kafka
bin/yacs-compose-development rm -vf zookeeper
bin/yacs-compose-development rm -vf redis
echo "Term.create(shortname: \"201809\", longname: \"Fall 2018\")" | bin/yacs-compose-development run --rm core rails c
echo "Term.create(shortname: \"201909\", longname: \"Fall 2019\")" | bin/yacs-compose-development run --rm core rails c
# echo "Term.create(shortname: \"201901\", longname: \"Spring 2019\")" | bin/yacs-compose-development run --rm core rails c
4 changes: 2 additions & 2 deletions core/app/consumers/application_consumer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class ApplicationConsumer < Karafka::BaseConsumer
subject: %i(shortname longname uuid school_uuid),
listing: %i(shortname longname description min_credits max_credits uuid subject_uuid subject_shortname) <<
{ required_textbooks: [], recommended_textbooks: [], tags: [] },
section: %i(shortname crn seats seats_taken uuid listing_uuid instructors) <<
{ periods: %i(day start end type location) }
section: %i(shortname crn seats seats_taken uuid listing_uuid) <<
{ instructors: [], periods: %i(day start end type location) }
}.with_indifferent_access.freeze

include Karafka::Consumers::Callbacks
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class AltSeatsAndSeatstakenToNullable < ActiveRecord::Migration[5.1]
def change
change_column_null :sections, :seats, true
change_column_default :sections, :seats, nil
change_column_null :sections, :seats_taken, true
change_column_default :sections, :seats_taken, nil
end

def down
raise ActiveRecord::IrreversibleMigration
end
end
6 changes: 3 additions & 3 deletions core/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20181031001113) do
ActiveRecord::Schema.define(version: 20190326210039) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -66,8 +66,8 @@
create_table "sections", id: :serial, force: :cascade do |t|
t.string "shortname", null: false
t.string "crn", null: false
t.integer "seats", null: false
t.integer "seats_taken", null: false
t.integer "seats"
t.integer "seats_taken"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "conflict_ids", default: [], null: false, array: true
Expand Down
33 changes: 19 additions & 14 deletions core/db/structure.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
*
* This purpose of this file is to house any custom plpgsql functions
* or other "raw" postgresql code that is needed to configure the database.
*
*
* Any changes or additions to such code should be handled via migration,
* using `20171210212207_add_conflict_ids_procedure.rb` as an example.
*
*
* This file is needed because although it is possible to execute "raw"
* sql code in a migration, this is not reflected in the generated schema.rb,
* which causes problems with testing and deployment.
*
*
* As such, this file should contain any and all "raw" sql code necessary
* to configure the database to the state of the **latest** migration.
*
*
* If you are getting odd test failures that could be related to such a
* misconfiguration, check to make sure any "raw" migrations are reflected
* here as well.
Expand All @@ -36,30 +36,35 @@ DECLARE
conflict_found BOOLEAN;
BEGIN
SELECT terms.id INTO this_term_id FROM terms
INNER JOIN listings ON listings.term_id = terms.id
INNER JOIN sections ON sections.listing_id = listings.id
LEFT OUTER JOIN listings ON listings.term_id = terms.id
LEFT OUTER JOIN sections ON sections.listing_id = listings.id
WHERE sections.id = section_id;
SELECT * INTO this_section FROM sections WHERE sections.id = section_id;
<<section_loop>>
FOR other_section IN
SELECT * FROM sections
INNER JOIN listings ON listings.id = sections.listing_id
INNER JOIN terms ON terms.id = listings.term_id
LEFT OUTER JOIN listings ON listings.id = sections.listing_id
LEFT OUTER JOIN terms ON terms.id = listings.term_id
WHERE sections.listing_id != this_section.listing_id
AND terms.id = this_term_id
LOOP
conflict_found := 'false';
<<outer_period_loop>>
FOR this_section_period IN SELECT * FROM jsonb_array_elements(this_section.periods) LOOP
<<inner_period_loop>>
FOR other_section_period IN SELECT * FROM jsonb_array_elements(other_section.periods) LOOP
IF (other_section_period->'day' = other_section_period->'day'
AND ((this_section_period->'start' <= other_section_period->'state' AND this_section_period->'end' > other_section_period->'start')
OR (this_section_period->'start' >= other_section_period->'start' AND this_section_period->'start' < other_section_period->'end')))
IF (this_section_period->>'day' = other_section_period->>'day'
AND (((this_section_period->>'start')::NUMERIC <= (other_section_period->>'start')::NUMERIC AND (this_section_period->>'end')::NUMERIC >= (other_section_period->>'start')::NUMERIC)
OR ((this_section_period->>'start')::NUMERIC >= (other_section_period->>'start')::NUMERIC AND (this_section_period->>'start')::NUMERIC <= (other_section_period->>'end')::NUMERIC)))
THEN
conflict_ids := conflict_ids || other_section.id;
conflict_found := 'true';
END IF;
END LOOP;
END LOOP;
END LOOP;
EXIT inner_period_loop WHEN conflict_found IS TRUE;
END LOOP inner_period_loop;
EXIT outer_period_loop WHEN conflict_found IS TRUE;
END LOOP outer_period_loop;
END LOOP section_loop;
RETURN conflict_ids;
END;
$$ LANGUAGE plpgsql;
4 changes: 2 additions & 2 deletions docker-compose.development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ services:
build: ./core
environment:
- RAILS_ENV=development
- TERM_SHORTNAME=201809
- TERM_SHORTNAME=201909
- UNI_SHORTNAME
volumes:
- ./core:/usr/src/app
Expand All @@ -47,7 +47,7 @@ services:
volumes:
- "./malg:/usr/src/app"
environment:
- TERM_SHORTNAME=201809
- TERM_SHORTNAME=201909
- UNI_SHORTNAME

notifications:
Expand Down
10 changes: 7 additions & 3 deletions pipelines/rpi/adapters/acalog-rpi/acalog_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,17 @@ def current_catalog_id

def catalog_id_for term_shortname
year, month = /(\d{4})(\d{2})/.match(term_shortname).captures.map &:to_i
catalogs_xml = request('content', method: :getCatalogs)
year -= 1 if month < 9
title = "Rensselaer Catalog #{year}-#{year + 1}"
node = request('content', method: :getCatalogs).
xpath("//catalog[title[contains(text(),\"#{title}\")]]/@id")
node = catalogs_xml.xpath("//catalog[title[contains(text(),\"#{title_for(year)}\")]]/@id")
node = catalogs_xml.xpath("//catalog[title[contains(text(),\"#{title_for(year - 1)}\")]]/@id") if node.empty?
@catalog_id = /acalog-catalog-(?<id>\d+)/.match(node.text)[:id].to_i
end

def title_for year
"Rensselaer Catalog #{year}-#{year + 1}"
end

def request path, params
params = params.merge({ key: @api_key, format: :xml })
uri = "#{@api_url}/v1/#{path}?#{params.to_query}"
Expand Down
7 changes: 5 additions & 2 deletions web/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { NavUserComponent } from './header/nav-user/component';
import { NoticeBarComponent } from './notice-bar/component';
import { FooterComponent } from './footer/component';
import { AboutComponent } from './about/component';
import { TermSelectorComponent } from './header/term-selector/component';

import { SidebarModule } from './sidebar/module';
import { SchoolViewModule } from './school-view/module';
Expand All @@ -25,10 +26,10 @@ import { ConflictsService } from './services/conflicts.service';
import { NoticeService } from './services/notice.service';
import { UserService } from './services/user.service';
import { ColorService } from './services/color.service';
import { SelectedTermService } from './services/selected-term.service';
import { SidebarService } from './services/sidebar.service';



@NgModule({
imports: [
BrowserModule,
Expand All @@ -50,14 +51,16 @@ import { SidebarService } from './services/sidebar.service';
NoticeBarComponent,
FooterComponent,
AboutComponent,
NavUserComponent
NavUserComponent,
TermSelectorComponent
],
providers: [
SelectionService,
ConflictsService,
NoticeService,
UserService,
ColorService,
SelectedTermService,
SidebarService
],
bootstrap: [AppComponent]
Expand Down
2 changes: 1 addition & 1 deletion web/src/app/footer/component.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<a href="/about">Learn more about YACS.</a>
</div>
<div class="footer-column">
<div class="version">YACS 0.10 - Angular</div>
<div class="version">YACS 0.12 - What A Long, Strange Trip It's Been</div>
<div class="feedback">
We love feedback!
<a href="github.com/yacs-rcos/yacs/issues" target="_blank">Github</a>
Expand Down
4 changes: 4 additions & 0 deletions web/src/app/header/component.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
<span class="theme-text theme-text-heavy">Schedule</span>
</a>
</li>
<!-- term selector -->
<li class="nav-item my-1">
<term-selector></term-selector>
</li>
</ul>
</div>
</div>
Expand Down
9 changes: 9 additions & 0 deletions web/src/app/header/term-selector/component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<a class="nav-link">
<a (click)="previousTerm()"><i class="arrow left" [class.arrow_disabled]="isFirstTerm"></i></a>&nbsp;
<span class="theme-text theme-text-heavy">
{{ internalName }}
<img *ngIf="!isActiveTerm" src="assets/images/eye.svg" placement="bottom" ngbPopover="Term is currently is view-only mode." alt="View Only" />
</span>&nbsp;

<a (click)="nextTerm()"><i class="arrow right" [class.arrow_disabled]="isLastTerm"></i></a>
</a>
24 changes: 24 additions & 0 deletions web/src/app/header/term-selector/component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

.arrow {
border: solid white;
border-width: 0 3px 3px 0;
display: inline-block;
padding: 3px;
cursor: pointer;
}

.arrow_disabled {
border: solid grey;
border-width: 0 3px 3px 0;
cursor: not-allowed;
}

.right {
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
}

.left {
transform: rotate(135deg);
-webkit-transform: rotate(135deg);
}
Empty file.
58 changes: 58 additions & 0 deletions web/src/app/header/term-selector/component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {Component, OnInit} from '@angular/core';
import {SelectedTermService} from '../../services/selected-term.service';
import {Term} from 'yacs-api-client';

@Component({
selector: 'term-selector',
templateUrl: './component.html',
styleUrls: ['./component.scss']
})
export class TermSelectorComponent implements OnInit {

// cache the name of the term
private internalName: string;

constructor(
private selectedTermService: SelectedTermService) {
// default the name to 'loading' until it loads
this.internalName = 'loading';
}

ngOnInit(): void {
this.selectedTermService.subscribeToTerm((term: Term) => {
this.internalName = term.longname;
});
}

get isFirstTerm(): boolean {
return this.selectedTermService.getCurrentOrdinal === 0;
}

get isLastTerm(): boolean {
return this.selectedTermService.getCurrentOrdinal === this.selectedTermService.getMaximumOrdinal;
}

get isActiveTerm(): boolean {
return this.selectedTermService.isCurrentTermActive;
}

/**
* Move to the previous (more recent) Term
*/
previousTerm() {
const ord = this.selectedTermService.getCurrentOrdinal;
if (ord > 0) {
this.selectedTermService.setSelectedTermByOrdinal(ord - 1);
}
}

/**
* Move to the next (less recent) Term
*/
nextTerm() {
const ord = this.selectedTermService.getCurrentOrdinal;
if (ord < this.selectedTermService.getMaximumOrdinal) {
this.selectedTermService.setSelectedTermByOrdinal(ord + 1);
}
}
}
38 changes: 31 additions & 7 deletions web/src/app/listing-view/component.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
import { Component, OnInit } from '@angular/core';
import {Component, OnDestroy, OnInit} from '@angular/core';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { Term, Course, Listing } from 'yacs-api-client';
import { YacsService } from '../services/yacs.service';
import { ConflictsService } from '../services/conflicts.service';
import { SelectedTermService } from '../services/selected-term.service';
import { Subscription } from 'rxjs';

@Component({
selector: 'listing-view',
templateUrl: './component.html',
styleUrls: ['./component.scss']
})
export class ListingViewComponent implements OnInit {
export class ListingViewComponent implements OnInit, OnDestroy {
listings: Listing[] = [];
isLoaded: boolean = false;
cachedTermId: string;
cachedQuery: Params;
termSubscription: Subscription;

constructor (
private yacsService : YacsService,
private activatedRoute: ActivatedRoute,
private conflictsService: ConflictsService) { }
private conflictsService: ConflictsService,
private selectedTermService: SelectedTermService) {
this.cachedTermId = this.selectedTermService.getCurrentTermId;
}

async getCourses (params: Params): Promise<void> {
async getCourses (params: Params, termId: string): Promise<void> {
this.isLoaded = false;
const term = await Term.first();
const query = Object.assign({ term_id: term.data.id }, params);
const query = Object.assign({ term_id: termId }, params);
Listing
.where(query)
.includes('sections')
Expand All @@ -38,7 +45,24 @@ export class ListingViewComponent implements OnInit {

ngOnInit (): void {
this.activatedRoute.queryParams.subscribe((params: Params) => {
this.getCourses(params);
this.cachedQuery = params;
// only operate if the selected term has been determined (prevents race condition)
if (this.cachedTermId !== undefined) {
this.getCourses(this.cachedQuery, this.cachedTermId);
}
});
this.termSubscription = this.selectedTermService.subscribeToTerm((term: Term) => {
this.cachedTermId = term.id;
// only operate if the query has been determined (prevents race condition)
if (this.cachedQuery !== undefined) {
this.getCourses(this.cachedQuery, this.cachedTermId);
}
});

}

ngOnDestroy(): void {
// unsubscribe from the term subscription when this listing is destroyed to prevent leaking
this.termSubscription.unsubscribe();
}
}
Loading

0 comments on commit ca0d728

Please sign in to comment.