Commit 8dce99d5 authored by Guillaume Jimenez's avatar Guillaume Jimenez

Added calendar to set holidays for TATs

parent 3ce01500
......@@ -22,7 +22,7 @@ const Admin = {
</v-dialog>
<!-- Import Sample Table-->
<data-table ref="importSamplestable" :fixed="false" table-title="Import Samples" initial-sort="sampleAccessionDate" :export-enabled="true"></data-table>
<data-table :disable-sticky-header="true" ref="importSamplestable" :fixed="false" table-title="Import Samples" initial-sort="sampleAccessionDate" :export-enabled="true"></data-table>
<br />
......@@ -137,7 +137,8 @@ const Admin = {
</v-dialog>
<!-- Users Table -->
<data-table ref="userTable" :fixed="false" :fetch-on-created="true" data-url="./getUserDataTable" table-title="NuCLIA Users" initial-sort="name" no-data-text="No Users">
<data-table :disable-sticky-header="true" ref="userTable" :fixed="false" :fetch-on-created="true"
data-url="./getUserDataTable" table-title="NuCLIA Users" initial-sort="name" no-data-text="No Users">
<v-fade-transition slot="action1">
<v-tooltip bottom>
<v-btn flat icon @click="addUser" slot="activator">
......@@ -154,7 +155,10 @@ const Admin = {
<v-list-tile-title>Add New User</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</data-table>
</data-table>
<holiday-picker class="pt-3" @save-success="handleHolidaysSaved">
</holiday-picker>
</div>
`,
......@@ -297,6 +301,10 @@ const Admin = {
cancelDeletion() {
this.deleteUserConfirmationDialogVisible = false;
},
handleHolidaysSaved() {
this.snackBarMessage = "Successfully updated the list of holidays";
this.snackBarVisible = true;
},
isNewUserDialogSaveDisabled() {
if (this.$refs.newUserForm === undefined) {
this.editUserDialogSaveDisabled = true;
......
Vue.component('holiday-picker', {
props: {
dataUrlRoot: {default: webAppRoot, type: String}
},
template: `<div>
<v-toolbar color="primary" dark>
<v-toolbar-title>UTSW Holidays</v-toolbar-title>
</v-toolbar>
<v-card>
<v-card-text>
<div class="subheading">Select holidays for the lab (so that they are not counted as business days). No need to select Sat. or Sun.</div>
<v-btn @click="resetSelected" color="error">Reset</v-btn>
<v-btn @click="saveSelected" color="success">Save</v-btn>
<v-container grid-list-xs>
<v-layout row wrap>
<v-flex xs2>
<div >Selected holidays for the year:</div>
<v-btn :color="year == currentYear ? 'warning' : 'primary lighten-2'"
@click="handleYearChanged(year)" v-for="year in years" :key="year">{{ year }}</v-btn>
<div class="pl-3 pt-3">
<div class="pb-2">Looking at {{ currentYear }}: </div>
<div v-for="selectedDate in currentYearSelectedDates" :key="selectedDate"> {{ selectedDate }}</div>
</div>
</v-flex>
<v-flex xs10 v-for="year in years" :key="year" v-show="currentYear == year">
<v-layout row wrap >
<v-flex v-for="(rangeData, index) in ranges[year]" :key="year + '-' + index" xs3 class="mb-2">
<v-date-picker class="elevation-0"
v-model="rangeData.selectedDates"
multiple
no-title
:min="rangeData.range[0]"
:max="rangeData.range[1]"
@input="concatAllSelectedDates"
></v-date-picker>
</v-flex>
</v-layout>
</v-flex>
</v-layout>
</v-container>
</v-card-text>
</v-card>
</div>`,
data() {
return {
currentYear: moment().year(),
format: "YYYY-MM-DD",
ranges: {},
selectedDates: [],
years: [],
maxYear: moment().year() + 2,
minYear: moment().year() - 2,
currentYearSelectedDates: []
}
},
mounted: function() {
this.ranges = this.dateRanges();
this.getSelectedDates();
},
methods: {
dateRanges() {
var ranges = {};
this.years = [];
for (var i = this.minYear; i <= this.maxYear; i++) {
this.years.push(i);
ranges[i] = [];
for (var m = 0; m < 12; m++) {
var firstOfMonth = moment([i, m]);
ranges[i + ""].push({selectedDates: [i + "-" + (m+1)], range:[firstOfMonth.format(this.format), firstOfMonth.endOf("month").format(this.format)]});
}
}
return ranges;
},
resetSelected(doEmpty) {
for (var i = this.minYear; i <= this.maxYear; i++) {
for (var m = 0; m < 12; m++) {
this.ranges[i][m].selectedDates = doEmpty ? [] : [i + "-" + (m+1)];
}
}
this.selectedDates = [];
this.currentYearSelectedDates = [];
this.getSelectedDates();
},
handleYearChanged(year) {
this.currentYear = year;
this.getCurrentYearSelectedDates();
},
concatAllSelectedDates() {
var selectedDates = [];
for (var i = this.minYear; i <= this.maxYear; i++) {
for (var m = 0; m < 12; m++) {
var dates = this.ranges[i][m].selectedDates;
if (dates) {
selectedDates = selectedDates.concat(dates);
}
}
}
this.selectedDates = selectedDates;
this.getCurrentYearSelectedDates();
},
getCurrentYearSelectedDates() {
this.currentYearSelectedDates = this.selectedDates.filter(d => d.indexOf(this.currentYear) == 0 && d.split("-").length == 3);
},
getSelectedDates() {
axios.get(webAppRoot + "/getSelectedHolidays", {
params: {
minYear: this.minYear,
maxYear: this.maxYear
}
})
.then(response => {
if (response.data.isAllowed) {
for (var i = this.minYear; i <= this.maxYear; i++) {
for (var m = 0; m < 12; m++) {
var month = ("0" + (m + 1)).slice(-2);
var datesForMonth = response.data.payload[i + "-" + month];
if (datesForMonth) {
this.ranges[i][m].selectedDates = datesForMonth;
}
//this.ranges[i][m].selectedDates = response.data.payload;
}
}
console.log(response.data.payload);
this.concatAllSelectedDates();
}
else {
this.handleDialogs(response, this.getSelectedDates);
}
})
.catch(error => {
console.log(error);
});
},
saveSelected() {
var validSelectedDates = this.selectedDates.filter(d => d.split("-").length == 3);
axios({
method: 'post',
url: webAppRoot + "/saveHolidays",
params: {
},
data: {
selectedDates: validSelectedDates
}
}).then(response => {
if (response.data.isAllowed && response.data.success) {
console.log("success");
this.$emit("save-success");
}
else {
this.handleDialogs(response.data, this.saveSelected);
}
}
).catch(error => {
if (error.response.status == 403) { // need to relogin
bus.$emit("login-needed", [this, this.saveSelected]);
}
else {
console.log(error);
bus.$emit("some-error", [this, error]);
}
}
);
},
handleDialogs(response, callback) {
if (response.isXss) {
bus.$emit("xss-error", [this, response.reason]);
}
else if (response.login || response.data) {
bus.$emit("login-needed", [this, callback])
}
else if (response.success === false) {
bus.$emit("some-error", [this, response.message]);
}
},
},
computed: {
},
created: function () {
},
watch: {
}
})
\ No newline at end of file
......@@ -64,8 +64,8 @@ Vue.component('main-menu', {
{ title: 'Home', iconBefore: 'home', name: 'Home', regularItem: true },
{ title: 'Order Details', skipRoute: true, regularItem: true, iconAfter: 'keyboard_arrow_right', projectOrderSearch: true },
{ title: 'Sample Coverage', name: 'SampleCoverageNoSampleNoChrom', regularItem: true },
{ title: 'Compare Coverage', name: 'CompareCoverage', regularItem: true },
{ title: 'Low Coverage', name: 'LowCoverageBrowser', regularItem: true },
// { title: 'Compare Coverage', name: 'CompareCoverage', regularItem: true },
// { title: 'Low Coverage', name: 'LowCoverageBrowser', regularItem: true },
{ title: 'Seq Run Details', name: 'SeqRunDetailsTable', regularItem: true },
{ title: 'Sample Details', name: 'SampleDetailsTable', regularItem: true },
{ title: 'Quality Metrics', name: 'QMetrics', regularItem: true },
......
......@@ -226,6 +226,9 @@ const Home = {
fontSize: this.chartFontSize
}
},
scaleY: {
maxValue: 45
},
crosshairX: {
plotLabel: {
headerText: '%data-labels',
......@@ -250,6 +253,7 @@ const Home = {
};
zingchart.render({
output: "canvas",
id: this.tatChartId,
data: this.dataConfig,
height: "100%"
......
......@@ -179,7 +179,7 @@ const SampleCoverage = {
<v-layout row wrap>
<v-flex :class="[isBoxPlotVisible ? 'xs8' : 'xs12']">
<data-table ref="lowCovTable" :fixed="false" :fetch-on-created="false" @need-manual-data="getSampleLowCoverageDataTable()"
table-title="Exons with Min Depth < 100X" :initial-sort="'chr'" no-data-text="No Data"
table-title="Exons with Median Depth < 100X" :initial-sort="'chr'" no-data-text="No Data"
:export-enabled="true">
</data-table>
</v-flex>
......
......@@ -3,8 +3,14 @@ package utsw.bicf.nucliavault.controller;
import java.io.IOException;
import java.text.ParseException;
import java.time.LocalDate;
import java.util.Enumeration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
......@@ -21,14 +27,14 @@ import org.springframework.web.bind.annotation.ResponseBody;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import utsw.bicf.nucliavault.clarity.api.utils.APIResponse;
import utsw.bicf.nucliavault.clarity.api.utils.TypeUtils;
import utsw.bicf.nucliavault.controller.serialization.AjaxResponse;
import utsw.bicf.nucliavault.controller.serialization.TargetPage;
import utsw.bicf.nucliavault.controller.serialization.vuetify.ExampleTableSummary;
import utsw.bicf.nucliavault.controller.serialization.vuetify.SampleDetailsTableSummary;
import utsw.bicf.nucliavault.controller.serialization.vuetify.SampleToImportSummary;
import utsw.bicf.nucliavault.controller.serialization.vuetify.Summary;
import utsw.bicf.nucliavault.controller.serialization.vuetify.UserTableSummary;
......@@ -37,6 +43,7 @@ import utsw.bicf.nucliavault.dao.SampleDAO;
import utsw.bicf.nucliavault.dao.SaveOrUpdateDAO;
import utsw.bicf.nucliavault.dao.SubjectDAO;
import utsw.bicf.nucliavault.dao.api.PipelineDAO;
import utsw.bicf.nucliavault.model.Holidays;
import utsw.bicf.nucliavault.model.NucliaUser;
import utsw.bicf.nucliavault.model.ResponseAPI;
import utsw.bicf.nucliavault.model.hybrid.SampleToImport;
......@@ -240,4 +247,80 @@ public class AdminController {
return ajaxResponse.createObjectJSON();
}
@RequestMapping(value = "/saveHolidays", method = RequestMethod.POST)
@ResponseBody
public String saveHolidays(Model model, HttpSession session, @RequestBody String data) throws Exception {
NucliaUser currentUser = (NucliaUser) session.getAttribute("user");
AjaxResponse ajaxResponse = new AjaxResponse();
if (currentUser != null && !currentUser.getAdmin()) {
TargetPage targetPage = new TargetPage(model);
ajaxResponse.setIsAllowed(false);
return targetPage.toJSONString();
}
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JsonNode dataNode = mapper.readTree(data);
ArrayNode selectedDates = (ArrayNode) dataNode.get("selectedDates");
Iterator<JsonNode> items = selectedDates.elements();
LocalDateTime now = LocalDateTime.now();
List<LocalDate> newSelectedDates = new ArrayList<LocalDate>();
while (items.hasNext()) {
String date = items.next().textValue();
LocalDate dateParsed = LocalDate.parse(date, TypeUtils.monthFormatter);
newSelectedDates.add(dateParsed);
}
Set<Integer> yearsToModify = newSelectedDates.stream().map(d -> d.getYear()).collect(Collectors.toSet());
if (yearsToModify != null && !yearsToModify.isEmpty()) {
modelDAO.removeAllDHolidaysForYears(yearsToModify);
}
for (LocalDate date : newSelectedDates) {
Holidays holidays = new Holidays();
holidays.setHolidayDate(date);
holidays.setModifiedBy(currentUser.getNucliaUserId());
holidays.setModifiedDate(now);
holidays.setYear(date.getYear());
modelDAO.saveObject(holidays);
}
ajaxResponse.setIsAllowed(true);
ajaxResponse.setSuccess(true);
return ajaxResponse.createObjectJSON();
}
@RequestMapping("/getSelectedHolidays")
@ResponseBody
public String getSelectedHolidays(Model model, HttpSession session, @RequestParam Integer minYear, @RequestParam Integer maxYear) throws Exception {
NucliaUser currentUser = (NucliaUser) session.getAttribute("user");
if (currentUser != null && !currentUser.getAdmin()) {
TargetPage targetPage = new TargetPage(model);
return targetPage.toJSONString();
}
try {
AjaxResponse ajaxResponse = new AjaxResponse();
List<Holidays> holidays = modelDAO.getAllHolidays(minYear, maxYear);
List<String> selectedDates = holidays.stream().map(h -> h.getHolidayDate().format(TypeUtils.monthFormatter)).collect(Collectors.toList());
Map<String, List<String>> selectedDatesByYearByMonth = new HashMap<String, List<String>>();
for (String selectedDate : selectedDates) {
String year = selectedDate.split("-")[0];
String month = selectedDate.split("-")[1];
String key = year + "-" + month;
List<String> dates = selectedDatesByYearByMonth.get(key);
if (dates == null) {
dates = new ArrayList<String>();
}
dates.add(selectedDate);
selectedDatesByYearByMonth.put(key, dates);
}
ajaxResponse.setIsAllowed(true);
ajaxResponse.setSuccess(true);
ajaxResponse.setPayload(selectedDatesByYearByMonth);
return ajaxResponse.createObjectJSON();
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
}
......@@ -22,6 +22,7 @@ import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
......@@ -546,5 +547,32 @@ public class APIController {
// }
// return "success";
// }
@RequestMapping(value="/updateEpicDate", produces="application/json; charset=utf-8", method=RequestMethod.POST)
@ResponseBody
public String updateEpicDate(Model model, HttpSession session,
@RequestParam String token, @RequestParam String orderId) throws JsonProcessingException {
session.setAttribute("user", "API User from updateEpicDate");
AjaxResponse response = new AjaxResponse();
Token theToken = pipelineDAO2.getAnswerToken(token);
if (theToken == null) {
response.setIsAllowed(false);
response.setMessage("You are not allowed to run this servlet.");
session.invalidate();
return response.createObjectJSON();
}
Subject subject = subjectDAO.getSubjectByLimsId(orderId);
if (subject != null) {
subject.setSentToEpicDate(LocalDateTime.now());
modelDAO.saveObject(subject);
response.setSuccess(true);
}
else {
response.setSuccess(false);
response.setMessage("No project with name: " + orderId);
}
session.invalidate();
return response.createObjectJSON();
}
}
......@@ -15,6 +15,7 @@ public class AjaxResponse {
Boolean isAllowed = true;
boolean success;
String message;
Object payload;
public Boolean getIsAllowed() {
return isAllowed;
......@@ -50,5 +51,17 @@ public class AjaxResponse {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(this);
}
public Object getPayload() {
return payload;
}
public void setPayload(Object payload) {
this.payload = payload;
}
}
......@@ -91,6 +91,7 @@ public class DataTableFilter {
sampleDetailsRnaFieldToColumnName.put("rnaExtractYieldValue", "rna_ex.rna_yield_ng");
sampleDetailsRnaFieldToColumnName.put("pctOver200ntValue", "rna_ex.perc_over_200_nt");
sampleDetailsRnaFieldToColumnName.put("rnaPreHybYieldAmount", "rna_lib.pre_hyb_yield");
sampleDetailsRnaFieldToColumnName.put("rnaPostHybLibMeanFragSize", "rna_lib.post_hyb_size");
sampleDetailsRnaFieldToColumnName.put("rnaPostHybMolarityValue", "rna_lib.hyb_molarity");
//Alignment
......
......@@ -183,7 +183,7 @@ public class CoverageDAO {
hql.append(" and e.is_tier_1 is true ");
}
hql.append(" and c.sample_id = :sampleId ")
.append(" and min_depth <= 100 ")
.append(" and median_depth <= 100 ")
.append(" group by e.exon_id, e.bait_pool_id, e.exon_name, min_depth, max_depth, median_depth, avg_depth, cstart, cend, chrom, is_tier_1, gene_symbol ")
.append(" order by e.cstart;");
List<SampleLowCoverageTable> coverages = session.createNativeQuery(hql.toString())
......
......@@ -32,7 +32,9 @@ import utsw.bicf.nucliavault.model.Demultiplex;
import utsw.bicf.nucliavault.model.DnaAlignment;
import utsw.bicf.nucliavault.model.DnaExtract;
import utsw.bicf.nucliavault.model.DnaHyb;
import utsw.bicf.nucliavault.model.Holidays;
import utsw.bicf.nucliavault.model.NucExtract;
import utsw.bicf.nucliavault.model.NucliaUser;
import utsw.bicf.nucliavault.model.Pipeline;
import utsw.bicf.nucliavault.model.ResponseAPI;
import utsw.bicf.nucliavault.model.RnaAlignment;
......@@ -42,9 +44,7 @@ import utsw.bicf.nucliavault.model.SeqRun;
import utsw.bicf.nucliavault.model.SomaticStats;
import utsw.bicf.nucliavault.model.TechnicianMarker;
import utsw.bicf.nucliavault.model.Threshold;
import utsw.bicf.nucliavault.model.NucliaUser;
import utsw.bicf.nucliavault.model.hybrid.FailureRate;
import utsw.bicf.nucliavault.model.hybrid.FailureRateTable;
import utsw.bicf.nucliavault.model.hybrid.LowCoverageTable;
import utsw.bicf.nucliavault.model.hybrid.MonthlyAlignmentTable;
import utsw.bicf.nucliavault.model.hybrid.MonthlySampleTAT;
......@@ -915,5 +915,27 @@ public class ModelDAO {
}
}).list();
}
@SuppressWarnings("rawtypes")
@Transactional
public void removeAllDHolidaysForYears(Set<Integer> yearsToModify) {
Session session = sessionFactory.getCurrentSession();
String sql = "delete from holidays where year in :yearsToModify";
Query query = session.createNativeQuery(sql).setParameter("yearsToModify", yearsToModify);
query.executeUpdate();
}
@Transactional
public List<Holidays> getAllHolidays(Integer minYear, Integer maxYear) {
Session session = sessionFactory.getCurrentSession();
String hql = "from Holidays where year >= :minYear and year <= :maxYear";
List<Holidays> holidays = session.createQuery(hql, Holidays.class)
.setParameter("minYear", minYear)
.setParameter("maxYear", maxYear)
.list();
return holidays;
}
}
package utsw.bicf.nucliavault.model;
import java.time.LocalDate;
import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="holidays")
public class Holidays {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="holidays_id")
Integer holidayId;
@Column(name="year")
Integer year;
@Column(name="holiday_date")
LocalDate holidayDate;
@Column(name="modified_by")
Integer modifiedBy;
@Column(name="modified_date")
LocalDateTime modifiedDate;
public Holidays() { }
public Integer getHolidayId() {
return holidayId;
}
public void setHolidayId(Integer holidayId) {
this.holidayId = holidayId;
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
public Integer getModifiedBy() {
return modifiedBy;
}
public void setModifiedBy(Integer modifiedBy) {
this.modifiedBy = modifiedBy;
}
public LocalDateTime getModifiedDate() {
return modifiedDate;
}
public void setModifiedDate(LocalDateTime modifiedDate) {
this.modifiedDate = modifiedDate;
}
public LocalDate getHolidayDate() {
return holidayDate;
}
public void setHolidayDate(LocalDate holidayDate) {
this.holidayDate = holidayDate;
}
}
package utsw.bicf.nucliavault.model;
import java.time.LocalDateTime;
import java.util.List;
import javax.persistence.CascadeType;
......@@ -51,6 +52,10 @@ public class Subject {
@Column(name="rna_tumor_sample")
String rnaTumorSample;
@Column(name="sent_to_epic_date")
LocalDateTime sentToEpicDate;
@JsonIgnore
@OneToMany(mappedBy="subject", fetch=FetchType.LAZY, cascade= {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
List<Sample> samples;
......@@ -135,4 +140,12 @@ public class Subject {
this.rnaTumorSample = rnaTumorSample;
}
public LocalDateTime getSentToEpicDate() {
return sentToEpicDate;
}
public void setSentToEpicDate(LocalDateTime sentToEpicDate) {
this.sentToEpicDate = sentToEpicDate;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment