@@ -115,13 +115,14 @@
+
diff --git a/src/components/SubjectTeachersList.vue b/src/components/SubjectTeachersList.vue
new file mode 100644
index 0000000..dcd4399
--- /dev/null
+++ b/src/components/SubjectTeachersList.vue
@@ -0,0 +1,297 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ scope.row.teacher.name }}
+
+
+ Professor desconhecido
+
+
+
+
+
+
+
+
+
+ Dados sem muitas amostras
+
+ Amostras: {{ scope.row.count }}
+
+
+
+
+
+
+
+
+
+
+
+ Dados sem muitas amostras
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/scripts/portal/error.svg b/src/images/error.svg
similarity index 100%
rename from app/scripts/portal/error.svg
rename to src/images/error.svg
diff --git a/app/images/icon-128.png b/src/images/icon-128.png
similarity index 100%
rename from app/images/icon-128.png
rename to src/images/icon-128.png
diff --git a/app/images/icon-16.png b/src/images/icon-16.png
similarity index 100%
rename from app/images/icon-16.png
rename to src/images/icon-16.png
diff --git a/app/images/icon-19.png b/src/images/icon-19.png
similarity index 100%
rename from app/images/icon-19.png
rename to src/images/icon-19.png
diff --git a/app/images/icon-38.png b/src/images/icon-38.png
similarity index 100%
rename from app/images/icon-38.png
rename to src/images/icon-38.png
diff --git a/app/scripts/portal/loading.svg b/src/images/loading.svg
similarity index 100%
rename from app/scripts/portal/loading.svg
rename to src/images/loading.svg
diff --git a/app/scripts/portal/logo-white.svg b/src/images/logo-white.svg
similarity index 100%
rename from app/scripts/portal/logo-white.svg
rename to src/images/logo-white.svg
diff --git a/app/scripts/popup/logo.svg b/src/images/logo.svg
similarity index 100%
rename from app/scripts/popup/logo.svg
rename to src/images/logo.svg
diff --git a/app/images/refresh.png b/src/images/refresh.png
similarity index 100%
rename from app/images/refresh.png
rename to src/images/refresh.png
diff --git a/app/images/refresh_small.png b/src/images/refresh_small.png
similarity index 100%
rename from app/images/refresh_small.png
rename to src/images/refresh_small.png
diff --git a/app/images/ring.gif b/src/images/ring.gif
similarity index 100%
rename from app/images/ring.gif
rename to src/images/ring.gif
diff --git a/src/lib/init.js b/src/lib/init.js
new file mode 100644
index 0000000..08eab28
--- /dev/null
+++ b/src/lib/init.js
@@ -0,0 +1,10 @@
+import Utils from "../utils/extensionUtils";
+
+xdLocalStorage.init({
+ /* required */
+ iframeUrl: Utils.getExtensionUrl("pages/iframe.html"),
+ //an option function to be called right after the iframe was loaded and ready for action
+ initCallback: function () {
+ console.log("Got iframe ready");
+ },
+});
diff --git a/app/scripts/lib/xdLocalStorage.min.js b/src/lib/xdLocalStorage.min.js
similarity index 100%
rename from app/scripts/lib/xdLocalStorage.min.js
rename to src/lib/xdLocalStorage.min.js
diff --git a/app/scripts/lib/xdLocalStoragePostMessageApi.min.js b/src/lib/xdLocalStoragePostMessageApi.min.js
similarity index 100%
rename from app/scripts/lib/xdLocalStoragePostMessageApi.min.js
rename to src/lib/xdLocalStoragePostMessageApi.min.js
diff --git a/src/main.js b/src/main.js
new file mode 100644
index 0000000..f2e8e39
--- /dev/null
+++ b/src/main.js
@@ -0,0 +1,98 @@
+import Vue from "vue";
+import Vuetify from "vuetify";
+import ElementUI from "element-ui";
+Vue.use(Vuetify);
+Vue.use(ElementUI);
+
+import Matricula from "./views/Matricula.vue";
+import ReviewTeacher from "./components/ReviewTeacher.vue";
+import ReviewSubject from "./components/ReviewSubject.vue";
+import Modal from "./components/Modal.vue";
+
+// global const change modal data
+const modalData = {
+ corte_id: null,
+ dialog: false,
+ disciplina: null,
+};
+
+const teacherReviewData = {
+ dialog: false,
+ professor: null,
+ // use this to notify
+ notifier: null,
+};
+
+const reviewSubjectData = {
+ dialog: false,
+ subject: null,
+ // use this to notify
+ notifier: null,
+};
+
+var app = new Vue({
+ el: "#app",
+ data: {
+ name: "ufabc-matricula-extension",
+ },
+ render: (h) => h(Matricula),
+});
+
+new Vue({
+ template:
+ '
',
+ el: "#modal",
+ data() {
+ return modalData;
+ },
+ components: { Modal },
+});
+
+new Vue({
+ template:
+ '
',
+ el: "#teacherReview",
+ data() {
+ return teacherReviewData;
+ },
+ components: { ReviewTeacher },
+});
+
+new Vue({
+ template:
+ '
',
+ el: "#review-subject",
+ data() {
+ return reviewSubjectData;
+ },
+ components: { ReviewSubject },
+});
+
+// handler cortes
+$("body").on("click", ".corte", async function (e) {
+ const target = $(e.target);
+ const corte_id = target.parent().parent().attr("value");
+ modalData.corte_id = corte_id;
+ modalData.dialog = true;
+});
+
+// handler teacherReview
+$("body").on("click", ".ReviewTeacher", function (e) {
+ const teacherId = $(e.target).attr("data");
+ const teacherName = $(e.target).attr("teacherName");
+ teacherReviewData.professor = {
+ id: teacherId,
+ name: teacherName,
+ };
+
+ teacherReviewData.dialog = true;
+});
+
+// handler subject click
+$("body").on("click", "span.sa, span.sbc", function (e) {
+ const subjectId = $(e.target).attr("subjectId");
+ reviewSubjectData.subject = {
+ id: subjectId,
+ };
+ reviewSubjectData.dialog = true;
+});
diff --git a/app/manifest.json b/src/manifest.json
similarity index 86%
rename from app/manifest.json
rename to src/manifest.json
index 4958408..5c827d8 100644
--- a/app/manifest.json
+++ b/src/manifest.json
@@ -13,7 +13,13 @@
"service_worker": "scripts/background.js"
},
"permissions": ["storage"],
- "host_permissions": ["*://*/*"],
+ "host_permissions": [
+ "http://*.ufabc.edu.br/*",
+ "https://*.ufabc.edu.br/*",
+ "http://localhost:8000/*",
+ "http://*.ufabcnext.com/*",
+ "https://*.ufabcnext.com/*"
+ ],
"content_scripts": [
{
"all_frames": true,
@@ -52,12 +58,16 @@
"web_accessible_resources": [
{
"resources": [
- "fonts/*",
- "scripts/*",
+ "components/*",
"images/*",
- "html/*",
+ "lib/*",
+ "pages/*",
+ "scripts/*",
+ "services/*",
"styles/*",
- "pages/*"
+ "utils/*",
+ "views/*",
+ "html/*"
],
"matches": [
"http://*.ufabc.edu.br/*",
diff --git a/src/pages/iframe.html b/src/pages/iframe.html
new file mode 100644
index 0000000..230e258
--- /dev/null
+++ b/src/pages/iframe.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+ UFABC Next Storage
+
+
diff --git a/app/pages/matricula/corte.html b/src/pages/matricula/corte.html
similarity index 100%
rename from app/pages/matricula/corte.html
rename to src/pages/matricula/corte.html
diff --git a/app/pages/matricula/fragments/matriculasNumber.html b/src/pages/matricula/fragments/matriculasNumber.html
similarity index 100%
rename from app/pages/matricula/fragments/matriculasNumber.html
rename to src/pages/matricula/fragments/matriculasNumber.html
diff --git a/app/pages/matricula/fragments/professorPopover.html b/src/pages/matricula/fragments/professorPopover.html
similarity index 100%
rename from app/pages/matricula/fragments/professorPopover.html
rename to src/pages/matricula/fragments/professorPopover.html
diff --git a/app/pages/popup.html b/src/pages/popup.html
similarity index 100%
rename from app/pages/popup.html
rename to src/pages/popup.html
diff --git a/app/scripts/background.js b/src/scripts/background.js
similarity index 100%
rename from app/scripts/background.js
rename to src/scripts/background.js
diff --git a/src/scripts/contentScriptPortal.js b/src/scripts/contentScriptPortal.js
new file mode 100644
index 0000000..01500a3
--- /dev/null
+++ b/src/scripts/contentScriptPortal.js
@@ -0,0 +1,252 @@
+import toastr from "toastr";
+import $ from "jquery";
+import _ from "lodash";
+import Utils from "../utils/extensionUtils";
+import { NextAPI } from "../services/NextAPI";
+import Axios from "axios";
+import Toastify from "toastify-js";
+import "toastify-js/src/toastify.css";
+const loading = require("../images/loading.svg");
+const errorSVG = require("../images/error.svg");
+const logoWhite = require("../images/logo-white.svg");
+
+const nextApi = NextAPI();
+
+const toast = new Toastify({
+ text: `
+
+
+
Atualizando suas informações...
\n\n
+
NÃO SAIA DESSA PÁGINA,
+
apenas aguarde, no máx. 5 min 🙏
+
`,
+ duration: -1,
+ close: false,
+ gravity: "bottom",
+ position: "right",
+ className: "toast-loading",
+ escapeMarkup: false,
+ avatar: loading,
+ style: {
+ background: "linear-gradient(to right, #2E7EED, rgba(46, 126, 237, 0.5));",
+ },
+});
+
+if (isIndexPortalAluno()) {
+ const anchor = document.createElement("div");
+ anchor.setAttribute("id", "app");
+ document.body.append(anchor);
+ Utils.injectScript("studentPortal.js");
+
+ Utils.injectStyle("styles/portal.css");
+ toastr.info(
+ "Clique em
Ficha Individual para atualizar suas informações!"
+ );
+} else if (isFichasIndividuaisPath()) {
+ Utils.injectStyle("styles/portal.css");
+
+ toast.showToast();
+
+ iterateTabelaCursosAndSaveToLocalStorage();
+} else if (isFichaIndividualPath()) {
+ Utils.injectStyle("styles/portal.css");
+}
+
+function isIndexPortalAluno() {
+ return (
+ document.location.href.indexOf("aluno.ufabc.edu.br/dados_pessoais") !== -1
+ );
+}
+
+function isFichasIndividuaisPath() {
+ return (
+ document.location.href.indexOf("aluno.ufabc.edu.br/fichas_individuais") !==
+ -1
+ );
+}
+
+function isFichaIndividualPath() {
+ return (
+ document.location.href.indexOf("aluno.ufabc.edu.br/ficha_individual") !== -1
+ );
+}
+
+function iterateTabelaCursosAndSaveToLocalStorage() {
+ var tabelaCursos = $("tbody").children().slice(1);
+ let count = 0;
+
+ tabelaCursos.each(async function () {
+ var linhaCurso = $(this).children();
+
+ var nomeDoCurso = $(linhaCurso[0]).children("a").text();
+ var fichaAlunoUrl = $(linhaCurso[1]).children("a").attr("href");
+ var anoDaGrade = $(linhaCurso[2]).text();
+
+ const curso = await getFichaAluno(fichaAlunoUrl, nomeDoCurso, anoDaGrade);
+ if (count == 0) toast.hideToast();
+ count++;
+
+ if (!curso) return;
+
+ curso.curso = linhaCurso[0].innerText.replace("Novo", "");
+ curso.turno = linhaCurso[3].innerText;
+
+ await saveToLocalStorage(curso);
+
+ await saveStudentsToLocalStorage(curso);
+ });
+}
+
+async function getFichaAluno(fichaAlunoUrl, nomeDoCurso, anoDaGrade) {
+ try {
+ var curso = {};
+ var ficha_url = fichaAlunoUrl.replace(".json", "");
+
+ const ficha = await Axios.get("https://aluno.ufabc.edu.br" + ficha_url, {
+ timeout: 60 * 1 * 1000, // 1 minute
+ });
+ const ficha_obj = $($.parseHTML(ficha.data));
+ const info = ficha_obj.find(".coeficientes tbody tr td");
+
+ const ra =
+ /.*?(\d+).*/g.exec(
+ ficha_obj.find("#page").children("p")[2].innerText
+ )[1] || "some ra";
+
+ const storageRA = "ufabc-extension-ra-" + getEmailAluno();
+ await Utils.storage.setItem(storageRA, ra);
+
+ const jsonFicha = await Axios.get(
+ "https://aluno.ufabc.edu.br" + fichaAlunoUrl,
+ {
+ timeout: 60 * 1 * 1000, // 1 minute
+ }
+ );
+
+ const disciplinasCategory = ficha_obj.find(
+ ".quantidades:last-child tbody tr td"
+ );
+
+ // free
+ const totalCreditsCoursedFree = toNumber(disciplinasCategory[2]);
+ const totalPercentageCoursedFree = toNumber(disciplinasCategory[3]);
+ const totalCreditsFree = Math.round(
+ (totalCreditsCoursedFree * 100) / totalPercentageCoursedFree
+ );
+
+ // mandatory
+ const totalCreditsCoursedMandatory = toNumber(disciplinasCategory[7]);
+ const totalPercentageCoursedMandatory = toNumber(disciplinasCategory[8]);
+ const totalCreditsMandatory = Math.round(
+ (totalCreditsCoursedMandatory * 100) / totalPercentageCoursedMandatory
+ );
+
+ // limited
+ const totalCreditsCoursedLimited = toNumber(disciplinasCategory[12]);
+ const totalPercentageCoursedLimited = toNumber(disciplinasCategory[13]);
+ const totalCreditsLimited = Math.round(
+ (totalCreditsCoursedLimited * 100) / totalPercentageCoursedLimited
+ );
+
+ await nextApi.post(
+ "/histories",
+ {
+ ra: ra,
+ disciplinas: jsonFicha.data,
+ curso: nomeDoCurso,
+ grade: anoDaGrade,
+
+ // credits total
+ mandatory_credits_number: totalCreditsMandatory,
+ limited_credits_number: totalCreditsLimited,
+ free_credits_number: totalCreditsFree,
+ credits_total:
+ totalCreditsMandatory + totalCreditsLimited + totalCreditsFree,
+ },
+ {
+ timeout: 60 * 1 * 1000, // 1 minute
+ }
+ );
+
+ curso.ra = ra;
+ curso.cp = toNumber(info[0]);
+ curso.cr = toNumber(info[1]);
+ curso.ca = toNumber(info[2]);
+ curso.quads = ficha_obj.find(".ano_periodo").length;
+
+ curso.cursadas = jsonFicha.data;
+
+ return curso;
+ } catch (err) {
+ console.log(err);
+ Toastify({
+ text: `
+
+
+ Não foi possível salvar seus dados, recarregue a página e aguarde.
+
`,
+ duration: -1,
+ close: true,
+ gravity: "top",
+ position: "right",
+ className: "toast-error-container",
+ style: {
+ background: "#E74C3C;",
+ },
+ }).showToast();
+ }
+}
+
+function getEmailAluno() {
+ return $("#top li")
+ .last()
+ .text()
+ .replace(/\s*/, "")
+ .split("|")[0]
+ .replace(" ", "")
+ .toLowerCase();
+}
+
+function toNumber(el) {
+ return parseFloat(el.innerText.replace(",", "."));
+}
+
+async function saveToLocalStorage(curso) {
+ const storageUser = "ufabc-extension-" + getEmailAluno();
+ let user = await Utils.storage.getItem(storageUser);
+ if (!user || _.isEmpty(user)) user = [];
+
+ user.push(curso);
+ user = _.uniqBy(user, "curso");
+
+ await Utils.storage.setItem(storageUser, user);
+
+ toastr.success(
+ `Suas informações foram salvas! Disciplinas do curso do ${curso.curso}
+ para o usuário ${getEmailAluno()}.
+ `,
+ { timeout: 100000 }
+ );
+}
+
+async function saveStudentsToLocalStorage(curso) {
+ const storageUser = "ufabc-extension-" + getEmailAluno();
+ const cursos = await Utils.storage.getItem(storageUser);
+ const ra = (curso && curso.ra) || null;
+
+ let allSavedStudents = [];
+ const students = await Utils.storage.getItem("ufabc-extension-students");
+ if (students && students.length) {
+ allSavedStudents.push(...students);
+ }
+
+ allSavedStudents = allSavedStudents.filter((student) => student.ra != ra);
+ const student = {
+ cursos: cursos,
+ ra: ra,
+ name: getEmailAluno(),
+ lastUpdate: Date.now(),
+ };
+ allSavedStudents.unshift(student);
+ await Utils.storage.setItem("ufabc-extension-students", allSavedStudents);
+}
diff --git a/src/scripts/contentscript.js b/src/scripts/contentscript.js
new file mode 100644
index 0000000..67b129a
--- /dev/null
+++ b/src/scripts/contentscript.js
@@ -0,0 +1,105 @@
+// https://crx.dam.io/ext/gphjopenfpnlnffmhhhhdiecgdcopmhk.html
+// add extension id to window
+const isBrowser = typeof chrome != "undefined" && !!chrome.storage;
+// var script = document.createElement('script');
+// const extension_id = isBrowser ? chrome.i18n.getMessage("@@extension_id") : null;
+// script.innerHTML = `extension_id = "${extension_id}"`;
+// (document.head || document.documentElement).appendChild(script)
+
+import $ from "jquery";
+import _ from "lodash";
+
+import matriculaUtils from "../utils/Matricula";
+import { setupStorage } from "../utils/setupStorage";
+
+import Utils from "../utils/extensionUtils";
+
+// CSS imports
+import "element-ui/lib/theme-chalk/index.css";
+
+let matricula_url;
+
+if (process.env.NODE_ENV == "production") {
+ matricula_url = [
+ "matricula.ufabc.edu.br/matricula",
+ "ufabc-matricula.cdd.naoseiprogramar.com.br/snapshot",
+ "api.ufabcnext.com/snapshot",
+ ];
+} else {
+ matricula_url = [
+ "matricula.ufabc.edu.br/matricula",
+ "api.ufabcnext.com/snapshot",
+ "api.ufabcnext.com/snapshot/backup.html",
+ "ufabc-matricula.cdd.naoseiprogramar.com.br/snapshot",
+ "ufabc-matricula.cdd.naoseiprogramar.com.br/snapshot/backup.html",
+ "ufabc-matricula-test.cdd.naoseiprogramar.com.br/snapshot",
+ "ufabc-matricula-test.cdd.naoseiprogramar.com.br/snapshot/backup.html",
+ "locahost:8011/snapshot",
+ "locahost:8011/snapshot/backup.html",
+ ];
+}
+
+if (!isBrowser) {
+ console.log("Not running on browser!");
+ load();
+} else {
+ window.addEventListener("load", load);
+}
+
+async function load() {
+ const currentUrl = document.location.href;
+ // add cross-domain local storage
+ Utils.injectScript("lib/xdLocalStorage.min.js");
+ Utils.injectIframe("pages/iframe.html");
+ Utils.injectScript("lib/init.js");
+
+ setupStorage();
+ require("./contentScriptPortal");
+
+ if (matricula_url.some((url) => currentUrl.indexOf(url) != -1)) {
+ // update teachers locally
+ setTimeout(async () => {
+ let lastUpdate = null;
+ try {
+ lastUpdate = await Utils.storage.getItem("ufabc-extension-last");
+ } catch (err) {
+ lastUpdate = Date.now();
+ } finally {
+ matriculaUtils.updateProfessors(lastUpdate);
+ }
+
+ // this is the main vue app
+ // i.e, where all the filters live
+ const anchor = document.createElement("div");
+ anchor.setAttribute("id", "app");
+ $("#meio").prepend(anchor);
+
+ //inject styles
+ Utils.injectStyle("styles/main.css");
+
+ // manda as informacoes para o servidor
+ matriculaUtils.sendAlunoData();
+
+ // load vue app modal
+ const modal = document.createElement("div");
+ modal.setAttribute("id", "modal");
+ modal.setAttribute("data-app", true);
+ document.body.append(modal);
+
+ // load vue app teacherReview
+ const teacherReview = document.createElement("div");
+ teacherReview.setAttribute("id", "teacherReview");
+ teacherReview.setAttribute("data-app", true);
+ document.body.append(teacherReview);
+
+ // load vue app review subjects
+ const reviewSubject = document.createElement("div");
+ reviewSubject.setAttribute("id", "review-subject");
+ reviewSubject.setAttribute("data-app", true);
+ document.body.append(reviewSubject);
+
+ // inject Vue app
+ Utils.injectScript("scripts/main.js");
+ }, 1500);
+ }
+}
diff --git a/app/scripts/popup.js b/src/scripts/popup.js
similarity index 93%
rename from app/scripts/popup.js
rename to src/scripts/popup.js
index 102f5ca..15c8ab3 100644
--- a/app/scripts/popup.js
+++ b/src/scripts/popup.js
@@ -18,7 +18,7 @@ import _ from 'lodash'
// })
import Vue from 'vue';
-import App from './popup/App.vue';
+import App from '../views/popup.vue';
var app = new Vue({
el: '#app',
diff --git a/src/services/NextAPI.js b/src/services/NextAPI.js
new file mode 100644
index 0000000..8647b06
--- /dev/null
+++ b/src/services/NextAPI.js
@@ -0,0 +1,30 @@
+import Axios from "axios";
+
+function resolveEndpoint(env) {
+ return (
+ {
+ development: "http://localhost:8011/v1",
+ staging: "https://ufabc-matricula-test.cdd.naoseiprogramar.com.br/v1",
+ production: "https://api.ufabcnext.com/v1",
+ }[env] || "http://localhost:8011/v1"
+ );
+}
+
+function NextAPI() {
+ const baseURL = resolveEndpoint(process.env.NODE_ENV);
+ const REQUEST_TIMEOUT = 5000;
+ const nextAPI = Axios.create({
+ baseURL,
+ timeout: REQUEST_TIMEOUT,
+ headers: {
+ Accept: "application/json",
+ "Content-Type": "application/json",
+ },
+ });
+
+ return nextAPI;
+}
+
+module.exports = {
+ NextAPI,
+};
diff --git a/src/studentPortal.js b/src/studentPortal.js
new file mode 100644
index 0000000..435907c
--- /dev/null
+++ b/src/studentPortal.js
@@ -0,0 +1,14 @@
+import Vue from "vue";
+import Vuetify from "vuetify";
+
+Vue.use(Vuetify);
+
+import Portal from "./views/Portal.vue";
+
+new Vue({
+ el: "#app",
+ data: {
+ name: "portal-matricula-extension",
+ },
+ render: (h) => h(Portal),
+});
diff --git a/app/styles/main.css b/src/styles/main.css
similarity index 100%
rename from app/styles/main.css
rename to src/styles/main.css
diff --git a/app/styles/portal.css b/src/styles/portal.css
similarity index 100%
rename from app/styles/portal.css
rename to src/styles/portal.css
diff --git a/src/utils/Matricula.js b/src/utils/Matricula.js
new file mode 100644
index 0000000..4efd35e
--- /dev/null
+++ b/src/utils/Matricula.js
@@ -0,0 +1,158 @@
+import Axios from "axios";
+import { NextAPI } from "../services/NextAPI";
+import toJSON from "./toJSON";
+import _ from "lodash";
+import $ from "jquery";
+import Utils from "./extensionUtils";
+
+const nextApi = NextAPI();
+
+module.exports = new Matricula();
+
+function Matricula() {
+ const MATRICULAS_URL =
+ {
+ development: "http://localhost:8011/snapshot/assets/todasDisciplinas.js",
+ staging:
+ "https://ufabc-matricula-test.cdd.naoseiprogramar.com.br/snapshot/assets/todasDisciplinas.js",
+ production: "https://matricula.ufabc.edu.br/cache/matriculas.js",
+ }[process.env.NODE_ENV] || "http://localhost:8011/v1";
+
+ // check if we need to update our localStorage of professors
+ // based when you did this last request
+ async function updateProfessors(data) {
+ let lastTime = data;
+ let timeDiff = (Date.now() - lastTime) / (1000 * 60);
+
+ await getProfessors();
+ if (!lastTime || timeDiff > 0.2) {
+ await getProfessors();
+ }
+ }
+
+ // fetch professors url and save them into localStorage
+ async function getProfessors() {
+ try {
+ let { data: professors } = await nextApi.get("/disciplinas");
+ await Utils.storage.setItem("ufabc-extension-last", Date.now());
+ await Utils.storage.setItem("ufabc-extension-disciplinas", professors);
+ return professors;
+ } catch (e) {
+ console.log("❌ Erro ao atualizar disciplinas");
+ console.error(e);
+ }
+ }
+
+ // disciplinas que mudaram de nome (HARDCODED)
+ var disciplinas_mudadas = {
+ "Energia: Origens, Conversão e Uso": "Bases Conceituais da Energia",
+ "Transformações nos Seres Vivos e Ambiente":
+ "Biodiversidade: Interações entre organismos e ambiente",
+ "Transformações Bioquímicas":
+ "Bioquímica: estrutura, propriedade e funções de Biomoléculas",
+ "Transformações Bioquímicas":
+ "Bioquímica: Estrutura, Propriedade e Funções de Biomoléculas",
+ "Origem da Vida e Diversidade dos Seres Vivos":
+ "Evolução e Diversificação da Vida na Terra",
+ };
+
+ // fetch matriculas again
+ async function getMatriculas() {
+ const disciplinas = await Axios.get(MATRICULAS_URL);
+ return toJSON(disciplinas.data) || {};
+ }
+
+ // get total number of matriculas that was made until now
+ async function getTotalMatriculas() {
+ return Object.keys(await getMatriculas()).length;
+ }
+
+ // get matriculas by aluno_id
+ async function getMatriculasAluno(aluno_id) {
+ const matriculas = await getMatriculas();
+ return _.get(matriculas, aluno_id);
+ }
+
+ // get current logged student
+ function getAlunoId() {
+ let toReturn = null;
+
+ $("script").each(function () {
+ var inside = $(this).text();
+ var test = "todasMatriculas";
+ if (inside.indexOf(test) != -1) {
+ var regex = /matriculas\[(.*)\]/;
+ var match = regex.exec(inside);
+ toReturn = parseInt(match[1]);
+ }
+ });
+
+ return toReturn;
+ }
+
+ // find courseId for this season
+ function findIdForCurso(name) {
+ if (
+ _.camelCase(name) == _.camelCase("Bacharelado em Ciências da Computação")
+ ) {
+ name = "Bacharelado em Ciência da Computação";
+ }
+ // normalize to camelCase
+ name = _.camelCase(name);
+
+ // check which row matches the name passed
+ const course = $("#curso")
+ .children()
+ .filter(function (i, item) {
+ return name == _.camelCase($(item).text());
+ })[0];
+
+ return $(course).val();
+ }
+
+ function currentUser() {
+ return $("#usuario_top")
+ .text()
+ .replace(/\s*/, "")
+ .split("|")[0]
+ .trim()
+ .toLowerCase();
+ }
+
+ // send aluno data
+ async function sendAlunoData() {
+ const storageUser = "ufabc-extension-" + currentUser();
+ const storageRA = "ufabc-extension-ra-" + currentUser();
+ const user = await Utils.storage.getItem(storageUser);
+ const ra = await Utils.storage.getItem(storageRA);
+
+ if (!user) return;
+
+ // remove as disciplinas cursadas
+ for (var i = 0; i < user.length; i++) {
+ delete user[i].cursadas;
+ }
+
+ await nextApi.post("/students", {
+ aluno_id: getAlunoId(),
+ cursos: user.map(function (info) {
+ info.curso_id = findIdForCurso(info.curso);
+ return info;
+ }),
+ ra: ra,
+ login: currentUser(),
+ });
+ }
+
+ return {
+ updateProfessors,
+ getProfessors,
+ getMatriculas,
+ getTotalMatriculas,
+ getMatriculasAluno,
+ getAlunoId,
+ findIdForCurso,
+ currentUser,
+ sendAlunoData,
+ };
+}
diff --git a/src/utils/convertUfabcDisciplina.js b/src/utils/convertUfabcDisciplina.js
new file mode 100644
index 0000000..f308eb6
--- /dev/null
+++ b/src/utils/convertUfabcDisciplina.js
@@ -0,0 +1,120 @@
+const _ = require("lodash");
+const removeDiacritics = require("./removeDiacritics");
+
+// This convert an disciplina from the .json from matriculas.ufabc
+function convertDisciplina(d) {
+ const obj = _.defaults(_.clone(d));
+
+ // specific for .json
+ delete obj.campus;
+ delete obj.turno;
+ obj.obrigatorias = _.map(obj.obrigatoriedades, "curso_id");
+
+ let afterNoon = false;
+
+ // handler horarios based on pdf or json
+ if (obj.horarios && _.isObject(obj.horarios)) {
+ let startHours = _.get(obj.horarios, "[0].horas", []);
+ let afterNoon = ["14:00", "15:00", "16:00", "17:00"].some((hour) =>
+ startHours.includes(hour)
+ );
+ } else if (obj.horarios && _.isString(obj.horarios)) {
+ obj.horarios = removeLineBreaks(obj.horarios);
+
+ const matched = obj.horarios.match(/\d{2}:\d{2}/g);
+
+ // only match if is even
+ if (matched.length % 2 == 0) {
+ let hours = _.chunk(matched, 2);
+ hours.forEach((m) => {
+ let [start, end] = m.map((h) => parseInt(h.split(":")[0]));
+
+ if (start >= 12 && start < 18) {
+ afterNoon = true;
+ }
+ });
+ }
+ }
+
+ // trabalha nas disciplinas
+ // if(!obj.nome) return obj
+
+ let turnoIndex = null;
+
+ let breakRule = "-";
+
+ var splitted = removeLineBreaks(obj.nome).split(breakRule);
+ if (splitted.length == 1) {
+ breakRule = " ";
+ splitted = splitted[0].split(/\s+/);
+ }
+
+ splitted.map(function (item, i) {
+ obj.campus = obj.campus || extractCampus(item);
+ obj.turno = obj.turno || (afterNoon ? "tarde" : extractTurno(item));
+
+ if ((obj.turno || obj.campus) && turnoIndex == null) turnoIndex = i;
+ });
+
+ if (!obj.campus) {
+ let secondPath = splitted.slice(turnoIndex + 1, splitted.length);
+ obj.campus = extractCampus(secondPath.join(breakRule));
+ }
+
+ // cut until the index we found
+ splitted = splitted.slice(0, turnoIndex);
+
+ // separa a turma da disciplina
+ var disciplina = _.compact(splitted.join("-").split(/\s+/));
+ obj.turma = disciplina[disciplina.length - 1];
+ disciplina.pop();
+
+ // fix disciplina
+ obj.disciplina = disciplina.join(" ").trim();
+ //obj.ideal_quad = app.helpers.season.findIdeais().includes(obj.codigo)
+
+ obj.disciplina_id = obj.id;
+ obj.codigo = obj.codigo;
+
+ return obj;
+}
+
+function cleanTeacher(str) {
+ return _.startCase(_.camelCase(str))
+ .replace(/-+.*?-+/g, "")
+ .replace(/\(+.*?\)+/g, "");
+}
+
+function removeLineBreaks(str = "") {
+ return str.replace(/\r?\n|\r/g, " ");
+}
+
+function extractTurno(d) {
+ const min = d.toLowerCase();
+ if (min.includes("diurno") || min.includes("matutino")) {
+ return "diurno";
+ }
+
+ if (min.includes("noturno")) {
+ return "noturno";
+ }
+
+ return null;
+}
+
+function extractCampus(d) {
+ const min = removeDiacritics(d.toLowerCase());
+ if (/.*santo\s+andre.*/.test(min)) {
+ return "santo andre";
+ }
+
+ if (/.*sao\s+bernardo.*/.test(min)) {
+ return "sao bernardo";
+ }
+
+ return null;
+}
+
+module.exports = {
+ convertDisciplina,
+};
diff --git a/src/utils/disciplinaIdentifier.js b/src/utils/disciplinaIdentifier.js
new file mode 100644
index 0000000..b8b4f1b
--- /dev/null
+++ b/src/utils/disciplinaIdentifier.js
@@ -0,0 +1,20 @@
+import { createHash } from "crypto";
+import _ from "lodash";
+
+const DEFAULT_FIELDS_TO_ENCODE = ["disciplina", "turno", "campus", "turma"];
+
+/**
+ * Generates a unique identifier for a given disciplina
+ * */
+function generateIdentifier(disciplina, keys = DEFAULT_FIELDS_TO_ENCODE) {
+ const unorderedDisciplinas = keys.map((key) => String(disciplina[key]));
+ const disciplinaToEncode = unorderedDisciplinas
+ .map((disciplina) => _.camelCase(disciplina))
+ .join("");
+
+ return createHash("md5").update(disciplinaToEncode).digest("hex");
+}
+
+module.exports = {
+ generateIdentifier,
+};
diff --git a/src/utils/extensionUtils.js b/src/utils/extensionUtils.js
new file mode 100644
index 0000000..0a30cf5
--- /dev/null
+++ b/src/utils/extensionUtils.js
@@ -0,0 +1,133 @@
+import $ from "jquery";
+import Axios from "axios";
+import is from "is_js";
+import _ from "lodash";
+
+module.exports = new ExtensionUtils();
+
+function ExtensionUtils() {
+ // force initialization of xdLocalStorage
+ // window.xdLocalStorage.init({
+ // iframeUrl: getExtensionUrl("pages/iframe.html"),
+ // });
+
+ const IS_BROWSER = typeof chrome != "undefined" && !!chrome.storage;
+ const EXTENSION_ID = IS_BROWSER
+ ? chrome.i18n.getMessage("@@extension_id")
+ : null;
+
+ var getChromeUrl = function (url) {
+ return getExtensionUrl(url);
+ };
+
+ var fetchChromeUrl = async function (url, cb) {
+ return Axios.get(getChromeUrl(url));
+ };
+
+ var injectDiv = async function (link, el) {
+ let resp = await fetchChromeUrl(link);
+ const data = resp.data;
+
+ var div = document.createElement("div");
+ div.innerHTML = data;
+
+ if (el) {
+ let parent = el.parentNode;
+ parent.insertBefore(div, el.nextSibling);
+ } else {
+ document.body.appendChild(div);
+ }
+ };
+
+ var injectStyle = function (link) {
+ var s = document.createElement("link");
+ s.href = getExtensionUrl(link);
+ s.type = "text/css";
+ s.rel = "stylesheet";
+
+ document.head.appendChild(s);
+ };
+
+ var injectScript = function (link) {
+ var s = document.createElement("script");
+ s.src = getExtensionUrl(link);
+
+ (document.head || document.documentElement).appendChild(s);
+ };
+
+ var injectIframe = function (link) {
+ var s = document.createElement("iframe");
+ s.src = getExtensionUrl(link);
+ s.setAttribute("style", "display: none;");
+ (document.body || document.documentElement).appendChild(s);
+ };
+
+ var storage = {
+ setItem(key, value) {
+ return new Promise((resolve, reject) => {
+ try {
+ const date = Date.now();
+ const event = new CustomEvent("requestStorage", {
+ detail: {
+ method: `setStorage-${key}-${date}`,
+ date: date,
+ key: key,
+ value: value,
+ },
+ });
+ document.addEventListener(`setStorage-${key}-${date}`, (evt) => {
+ resolve(evt.detail.value);
+ });
+ document.dispatchEvent(event);
+ } catch (err) {
+ console.error(err);
+ }
+ });
+ },
+ getItem(key) {
+ return new Promise((resolve, reject) => {
+ try {
+ const date = Date.now();
+ const event = new CustomEvent("requestStorage", {
+ detail: {
+ method: `getStorage-${key}-${date}`,
+ key: key,
+ date: date,
+ },
+ });
+ document.addEventListener(`getStorage-${key}-${date}`, (evt) => {
+ resolve(evt.detail.value);
+ });
+ document.dispatchEvent(event);
+ } catch (err) {
+ console.error(err);
+ }
+ });
+ },
+ };
+
+ function getExtensionUrl(link) {
+ if (EXTENSION_ID) {
+ const prefix = is.chrome() ? "chrome-extension://" : "moz-extension://";
+ return prefix + EXTENSION_ID + "/" + link.replace(/^\//, "");
+ } else {
+ return `https://next-extension.captain.sv.ufabcnext.com/static/${link}`;
+ }
+ }
+
+ var getFile = async function (link) {
+ return (await Axios.get(getChromeUrl(link))).data;
+ };
+
+ return {
+ getChromeUrl,
+ injectScript,
+ injectDiv,
+ injectIframe,
+ injectStyle,
+ fetchChromeUrl,
+ getExtensionUrl,
+ getFile,
+ storage,
+ };
+}
diff --git a/app/scripts/helpers/removeDiacritics.js b/src/utils/removeDiacritics.js
similarity index 100%
rename from app/scripts/helpers/removeDiacritics.js
rename to src/utils/removeDiacritics.js
diff --git a/src/utils/season.js b/src/utils/season.js
new file mode 100644
index 0000000..d07483b
--- /dev/null
+++ b/src/utils/season.js
@@ -0,0 +1,84 @@
+function findQuadFromDate(month) {
+ if ([0, 1, 2, 10, 11].includes(month)) {
+ return 1;
+ }
+ if ([3, 4, 5].includes(month)) {
+ return 2;
+ }
+ if ([6, 7, 8, 9].includes(month)) {
+ return 3;
+ }
+}
+
+function findSeason(date = new Date()) {
+ const month = date.getMonth();
+ return {
+ 1: {
+ quad: 1,
+ year: date.getFullYear() + (month < 6 ? 0 : 1),
+ },
+ 2: {
+ quad: 2,
+ year: date.getFullYear(),
+ },
+ 3: {
+ quad: 3,
+ year: date.getFullYear(),
+ },
+ }[findQuadFromDate(date.getMonth() || month)];
+}
+
+function findSeasonKey(date) {
+ const { year, quad } = findSeason(date);
+ return `${year}:${quad}`;
+}
+
+function findIdeais(date) {
+ return {
+ 1: [
+ "BCM0506-15", // COMUNICACAO E REDES
+ "BCJ0203-15", // ELETROMAG
+ "BIN0406-15", // IPE
+ "BCN0405-15", // IEDO
+ "BIR0004-15", // EPISTEMOLOGICAS
+ "BHO0102-15", // DESENVOL. E SUSTE.
+ "BHO0002-15", // PENSA. ECONOMICO
+ "BHP0201-15", // TEMAS E PROBLEMAS
+ "BHO0101-15", // ESTADO E RELA
+ "BIR0603-15", // CTS
+ "BHQ0003-15", // INTEPRE. BRASIL
+ "BHQ0001-15", // IDENT.E CULTURA
+ ],
+ 2: [
+ "BCM0504-15", // NI
+ "BCN0404-15", // GA
+ "BCN0402-15", // FUV
+ "BCJ0204-15", // FEMEC
+ "BCL0306-15", // BIODIVERSIDADE
+ "BCK0103-15", // QUANTICA
+ "BCL0308-15", // BIOQUIMICA
+ "BIQ0602-15", // EDS
+ "BHO1335-15", // FORMACAO SISTEMA INTERNACIONAL
+ "BHO1101-15", // INTRODUCAO A ECONOMIA
+ "BHO0001-15", // INTRODUCAO AS HUMANIDADES
+ "BHP0202-15", // PENSAMENTO CRITICO
+ ],
+ 3: [
+ "BCJ0205-15", // FETERM
+ "BCM0505-15", // PI
+ "BCN0407-15", // FVV
+ "BCL0307-15", // TQ
+ "BCK0104-15", // IAM
+ "BIR0603-15", // CTS
+ "BHP0001-15", // ETICA E JUSTICA
+ "BHQ0301-15", // TERRITORIO E SOCIEDADE
+ // ESTUDO ÉTNICOS RACIAIS
+ ],
+ }[findQuadFromDate(date || new Date().getMonth())];
+}
+
+module.exports = {
+ findSeason,
+ findSeasonKey,
+ findIdeais,
+};
diff --git a/src/utils/setupStorage.js b/src/utils/setupStorage.js
new file mode 100644
index 0000000..f90fd93
--- /dev/null
+++ b/src/utils/setupStorage.js
@@ -0,0 +1,69 @@
+function setupStorage() {
+ document.addEventListener("requestStorage", (event) => {
+ const key = event.detail.key;
+ const date = event.detail.date;
+ const value = event.detail.value;
+ const method = event.detail.method.split("-")[0];
+ const eventType = event.type;
+ if (!key || !date || !method || eventType != "requestStorage") return;
+
+ const IS_BROWSER = typeof chrome != "undefined" && !!chrome.storage;
+ const eventMethod = event.detail.method;
+ if (IS_BROWSER) {
+ console.log(`[${method} | ${key}] Using chrome.storage 🔵`);
+ // maybe below is actually resolve(data && data[key]) - please check
+
+ if (method == "setStorage") {
+ chrome.storage.local.set({ [key]: value });
+ return document.dispatchEvent(
+ new CustomEvent(eventMethod, {
+ detail: {
+ key: key,
+ value: value,
+ },
+ })
+ );
+ } else if (method == "getStorage") {
+ return chrome.storage.local.get(key, (data) => {
+ document.dispatchEvent(
+ new CustomEvent(eventMethod, {
+ detail: {
+ key: key,
+ value: data && data[key],
+ },
+ })
+ );
+ });
+ }
+ }
+
+ console.log(`[${method} | ${key}] Using xdLocalStorage 🔴`);
+ if (method == "setStorage") {
+ window.xdLocalStorage.setItem(key, JSON.stringify(value), (data) => {
+ document.dispatchEvent(
+ new CustomEvent(eventMethod, {
+ detail: {
+ key: key,
+ value: date.value,
+ },
+ })
+ );
+ });
+ } else if (method == "getStorage") {
+ window.xdLocalStorage.getItem(key, (data) => {
+ document.dispatchEvent(
+ new CustomEvent(eventMethod, {
+ detail: {
+ key: key,
+ value: JSON.parse(data.value),
+ },
+ })
+ );
+ });
+ }
+ });
+}
+
+module.exports = {
+ setupStorage,
+};
diff --git a/src/utils/toJSON.js b/src/utils/toJSON.js
new file mode 100644
index 0000000..56f5bd2
--- /dev/null
+++ b/src/utils/toJSON.js
@@ -0,0 +1,17 @@
+import _ from "lodash";
+
+const toJSON = (payload, max) => {
+ const json = JSON.parse(
+ _.get(new RegExp(/^\w*=(.*)/).exec(payload), "[1]", {})
+ );
+
+ if (max) {
+ return json.slice(0, max);
+ }
+
+ return json;
+};
+
+module.exports = {
+ toJSON,
+};
diff --git a/src/views/Matricula.vue b/src/views/Matricula.vue
new file mode 100644
index 0000000..684541e
--- /dev/null
+++ b/src/views/Matricula.vue
@@ -0,0 +1,323 @@
+
+
+
+
+
+
+
Câmpus
+
+
+ {{ filter.name }}
+
+
+
+
+
+
Turno
+
+
+ {{ filter.name }}
+
+
+
+
+
+
Filtros
+
+
+
+
+
+
+
+ Faz mais de uma semana que você não sincroniza seus dados.
+ Isso pode acabar afetando a ordem dos chutes.
+
+ Atualizar dados agora
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/Portal.vue b/src/views/Portal.vue
new file mode 100644
index 0000000..bd204f9
--- /dev/null
+++ b/src/views/Portal.vue
@@ -0,0 +1,9 @@
+
+ Estou no portal do aluno
+
+
+
diff --git a/app/scripts/popup/App.vue b/src/views/popup.vue
similarity index 96%
rename from app/scripts/popup/App.vue
rename to src/views/popup.vue
index 6db9dc7..e4f0053 100644
--- a/app/scripts/popup/App.vue
+++ b/src/views/popup.vue
@@ -1,6 +1,6 @@