Skip to content

Commit

Permalink
Staging (#38)
Browse files Browse the repository at this point in the history
* implemented some new stats. addresses #10

* switched to plotly for plots, implemented new stats and added raw export (#33)

* npm's plotly is broken, so imported via cdn

* switched to plotly for plots, implemented new stats and added raw export

* npm's plotly is broken, so imported via cdn

* Dev (#37)

* switched to plotly for plots, implemented new stats and added raw export

* npm's plotly is broken, so imported via cdn

* restrict access to raw data to companies and admin users

* resolve #36
  • Loading branch information
Maronato authored Jul 31, 2018
1 parent b10b7b2 commit 1026ee6
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 77 deletions.
3 changes: 1 addition & 2 deletions hacker/exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from django.contrib.auth.models import User
from rest_condition import Or
from godmode.permissions import IsAdmin
from staff.permissions import IsStaff
from company.permissions import EmployeeHasAccess
from project.mixins import ExportMixin
from project.generics import PrefetchListAPIView
Expand All @@ -28,5 +27,5 @@ def get_queryset(self):

class ExportAllHackers(ExportMixin, PrefetchListAPIView):
serializer_class = ExportAllHackersSerializer
permission_classes = [Or(IsAdmin, IsStaff, EmployeeHasAccess)]
permission_classes = [Or(IsAdmin, EmployeeHasAccess)]
queryset = User.objects.exclude(profile=None)
1 change: 1 addition & 0 deletions schedule/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def get_context_data(self, **kwargs):
schedule_context = {
'api': {
'list_events': reverse('schedule:api:event-list'),
'get_event': reverse('schedule:api:event-detail', args=[0])[:-2],
'create_event': reverse('schedule:api:event-list'),
'delete_event': reverse('schedule:api:event-detail', args=[0])[:-2],

Expand Down
12 changes: 10 additions & 2 deletions schedule/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class EventSerializer(
PrefetchMixin,
serializers.ModelSerializer):

speaker_unique_id = serializers.CharField(write_only=True, required=False)
speaker_unique_id = serializers.CharField(write_only=True, required=False, allow_null=True)
speaker = SimpleProfileSerializer(read_only=True)
n_attendees = serializers.IntegerField(read_only=True)
n_attended = serializers.IntegerField(read_only=True)
Expand Down Expand Up @@ -44,10 +44,18 @@ def create(self, validated_data):

def update(self, instance, validated_data):
# If switching from requiring register to not, clear relations
if not validated_data.get('require_register', True) and instance.require_register:
require_register = validated_data.get('require_register', None)
if require_register is not None and not require_register and instance.require_register:
print("NONNONNONONO")
instance.attendees.clear()
instance.attended.clear()
instance.feedbacks.all().delete()
unique_id = validated_data.pop('speaker_unique_id', False)
if unique_id or unique_id is None:
if unique_id is None:
validated_data['speaker'] = None
else:
validated_data['speaker'] = Profile.objects.get(unique_id=unique_id)
return super().update(instance, validated_data)

class Meta:
Expand Down
237 changes: 166 additions & 71 deletions schedule/static/schedule/components/sections/create/list.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,100 +34,192 @@
</strong>
</td>
<td class="right aligned collapsing">
<sui-button class="actionbuttons" :loading="loadingModal" :disabled="loadingModal" size="small" color="blue" content="Alterar Palestrante" @click="changeSpeaker(event)" />
<sui-button class="actionbuttons" size="small" color="red" content="Apagar" @click="deleteEvent(event)" />
</td>
</tr>
</tbody>
</table>
<sui-modal v-model="showSpeakerModal" size="small">

<sui-modal-header>Alterar palestrante de {{ selectedEvent.name }}</sui-modal-header>
<sui-modal-content>
<sui-modal-description>
<p v-if="selectedEvent.speaker">O palestrante atual é {{ selectedEvent.speaker.full_name }} ({{ selectedEvent.speaker.email }})</p>
<p>Selecionar <span v-if="selectedEvent.speaker">outro </span>palestrante?</p>
</sui-modal-description>
<select id="speaker_modal" class="ui search selection dropdown" placeholder="Selecione alguém"></select>
</sui-modal-content>
<sui-modal-actions>
<sui-button content="Remover palestrante" negative labelPosition="right" icon="times" className="" @click="removeSpeaker(selectedEvent.id)" :loading="loadingButton" :disabled="loadingButton" v-if="selectedEvent.speaker" />
<sui-button content="Definir palestrante" positive labelPosition="right" icon="check" className="" @click="setSpeaker(selectedEvent.id)" :loading="loadingButton" :disabled="!selectedSpeaker || loadingButton" />
</sui-modal-actions>
</sui-modal>
</div>
</template>

<script>
import axios from 'project/js/axios_csrf'
import toast from 'project/js/notifications'
import swal from 'sweetalert'
import { ModelSubscription } from 'model_sockets/js/subscription'
import axios from "project/js/axios_csrf";
import toast from "project/js/notifications";
import swal from "sweetalert";
import { ModelSubscription } from "model_sockets/js/subscription";
export default {
props: ["schedule_context"],
data() {
return {
schedule: this.schedule_context,
eventList: [],
searchText: '',
};
},
computed: {
filteredEvents() {
return this.eventList.filter(event => {
return (
event.name
export default {
props: ["schedule_context"],
data() {
return {
schedule: this.schedule_context,
eventList: [],
searchText: "",
selectedEvent: {},
showSpeakerModal: false,
loadingModal: false,
loadingButton: false,
selectedSpeaker: {}
};
},
computed: {
filteredEvents() {
return this.eventList.filter(event => {
return (
event.name
.toLowerCase()
.indexOf(this.searchText.toLowerCase()) > -1
);
});
}
},
methods: {
getEventList() {
var comp = this;
axios.get(this.schedule.api.list_events)
.then(function (data) {
);
});
}
},
methods: {
getEventList() {
var comp = this;
axios
.get(this.schedule.api.list_events)
.then(function(data) {
comp.eventList = data.data;
})
.catch(function (error) {
.catch(function(error) {
console.error(error);
toast('Opa!', 'Algo de errado aconteceu :(', 'error');
toast("Opa!", "Algo de errado aconteceu :(", "error");
});
},
deleteEvent(event) {
self = this;
swal({
title: 'Apagar ' + event.name + '?',
text: 'Não pode ser revertido!',
dangerMode: true,
icon: 'warning',
buttons: ["Cancelar", "Apagar"]
})
.then(apagar => {
if (apagar) {
axios.delete(self.schedule.api.delete_event + event.id + '/')
},
deleteEvent(event) {
self = this;
swal({
title: "Apagar " + event.name + "?",
text: "Não pode ser revertido!",
dangerMode: true,
icon: "warning",
buttons: ["Cancelar", "Apagar"]
}).then(apagar => {
if (apagar) {
axios
.delete(self.schedule.api.delete_event + event.id + "/")
.then(data => {
toast('Atenção!', 'Evento apagado', 'info');
toast("Atenção!", "Evento apagado", "info");
})
.catch(function (error) {
.catch(function(error) {
console.error(error);
toast('Opa!', 'Algo de errado aconteceu :(', 'error');
toast(
"Opa!",
"Algo de errado aconteceu :(",
"error"
);
});
}
}
});
},
eventUpdated(event) {
let eventIdx = this.eventList.findIndex(obj => obj.id == event.id);
this.eventList.splice(eventIdx, 1, event);
},
eventCreated(event) {
this.eventList.push(event);
},
eventDeleted(event) {
let eventIdx = this.eventList.findIndex(obj => obj.id == event.id);
this.eventList.splice(eventIdx, 1);
},
changeSpeaker(event) {
this.loadingModal = true;
this.selectedEvent = {};
var comp = this;
axios
.get(this.schedule.api.get_event + event.id + "/")
.then(data => {
comp.selectedEvent = data.data;
comp.loadingModal = false;
comp.showSpeakerModal = true;
$("#speaker_modal").dropdown({
apiSettings: {
url:
comp.schedule.api.sui_list_profiles +
"?search={query}",
cache: false
},
filterRemoteData: false,
saveRemoteData: false,
onChange: function(value) {
comp.selectedSpeaker = value;
}
});
})
},
eventUpdated(event) {
let eventIdx = this.eventList.findIndex((obj => obj.id == event.id));
this.eventList.splice(eventIdx, 1, event);
},
eventCreated(event) {
this.eventList.push(event);
},
eventDeleted(event) {
let eventIdx = this.eventList.findIndex((obj => obj.id == event.id));
this.eventList.splice(eventIdx, 1);
},
.catch(function(error) {
console.error(error);
toast("Opa!", "Algo de errado aconteceu :(", "error");
});
},
mounted() {
this.getEventList();
removeSpeaker(event_id) {
var comp = this;
this.loadingButton = true;
axios.patch(this.schedule.api.get_event + event_id + '/', {
speaker_unique_id: null
})
.then(data => {
comp.showSpeakerModal = false;
toast("Atenção!", "Palestrante removido", "info");
})
.catch(function(error) {
console.error(error);
toast("Opa!", "Algo de errado aconteceu :(", "error");
})
.then(d => {
comp.loadingButton = false;
})
},
setSpeaker(event_id) {
var comp = this;
this.loadingButton = true;
axios.patch(this.schedule.api.get_event + event_id + '/', {
speaker_unique_id: this.selectedSpeaker
})
.then(data => {
comp.showSpeakerModal = false;
toast("Sucesso!", "Palestrante alterado", "success");
})
.catch(function(error) {
console.error(error);
toast("Opa!", "Algo de errado aconteceu :(", "error");
})
.then(d => {
comp.loadingButton = false;
})
},
},
mounted() {
this.getEventList();
var updatesub = new ModelSubscription('schedule', 'Event', 'update');
var createsub = new ModelSubscription('schedule', 'Event', 'create');
var deletesub = new ModelSubscription('schedule', 'Event', 'delete');
updatesub.connect();
createsub.connect();
deletesub.connect();
updatesub.subscribe(this.eventUpdated);
createsub.subscribe(this.eventCreated);
deletesub.subscribe(this.eventDeleted);
}
};
var updatesub = new ModelSubscription("schedule", "Event", "update");
var createsub = new ModelSubscription("schedule", "Event", "create");
var deletesub = new ModelSubscription("schedule", "Event", "delete");
updatesub.connect();
createsub.connect();
deletesub.connect();
updatesub.subscribe(this.eventUpdated);
createsub.subscribe(this.eventCreated);
deletesub.subscribe(this.eventDeleted);
}
};
</script>

<style scoped>
Expand All @@ -149,4 +241,7 @@
.ui.table {
font-size: 0.9em;
}
.ui.modal > .content {
text-align: center;
}
</style>
26 changes: 24 additions & 2 deletions stats/static/stats/components/stats.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<div class="ui stackable centered page grid">
<div class="row">
<div class="column">
<sui-menu class="stackable three item">
<sui-menu :class="nTabs" class="stackable item">
<a
is="sui-menu-item"
v-for="item in items"
Expand All @@ -26,6 +26,7 @@
v-if="isActive('Aplicações')" />
<RawData
v-bind:stats_context="stats"
v-if="permissionToRawData"
v-show="isActive('Dados brutos')" />

</div>
Expand All @@ -47,7 +48,6 @@
user: this.user_context,
stats: this.stats_context,
active: "Participantes",
items: ["Participantes", "Aplicações", "Dados brutos"]
};
},
methods: {
Expand All @@ -57,6 +57,28 @@
select(name) {
this.active = name;
}
},
computed: {
permissionToRawData() {
if (this.user.is_admin) {
return true;
}
if (this.user.employee_company_access >= 0) {
return true;
}
return false;
},
nTabs() {
if (this.permissionToRawData)
return "three";
return "two";
},
items() {
let items = ['Participantes', 'Aplicações'];
if (this.permissionToRawData)
items.push('Dados brutos');
return items;
}
}
};
</script>

0 comments on commit 1026ee6

Please sign in to comment.