Merge branch 'master' into production
This commit is contained in:
commit
7df7386a97
16 changed files with 280 additions and 15 deletions
|
@ -1,4 +1,13 @@
|
|||
FROM golang:latest
|
||||
############################
|
||||
# STEP 1 build executable binary
|
||||
############################
|
||||
|
||||
FROM golang:alpine AS builder
|
||||
|
||||
# Install git.
|
||||
# Git is required for fetching the dependencies.
|
||||
|
||||
RUN apk update && apk add --no-cache git
|
||||
|
||||
ENV PATH=$PATH:/src/oef/
|
||||
RUN mkdir -p /src/oef
|
||||
|
@ -8,7 +17,48 @@ COPY config/config.aws.yaml /src/oef/config/config.yaml
|
|||
|
||||
WORKDIR /src/oef/
|
||||
|
||||
RUN go build -o oef *.go
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o oef *.go
|
||||
# RUN go build -o oef *.go
|
||||
|
||||
############################
|
||||
# STEP 2 build a small image
|
||||
############################
|
||||
|
||||
FROM scratch
|
||||
|
||||
# Copy our static executable.
|
||||
COPY --from=builder /src/oef/oef /src/oef/oef
|
||||
|
||||
# Copy config file
|
||||
COPY --from=builder /src/oef/config/config.yaml /src/oef/config/config.yaml
|
||||
|
||||
# Copy templates
|
||||
COPY --from=builder /src/oef/templates /src/oef/templates
|
||||
|
||||
# Copy dist
|
||||
COPY --from=builder /src/oef/dist /src/oef/dist
|
||||
|
||||
# Copy VERSION file
|
||||
COPY --from=builder /src/oef/VERSION /src/oef/VERSION
|
||||
|
||||
WORKDIR /src/oef/
|
||||
|
||||
# Run the oef binary.
|
||||
ENTRYPOINT ["/src/oef/oef"]
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
# FROM golang:latest
|
||||
|
||||
# ENV PATH=$PATH:/src/oef/
|
||||
# RUN mkdir -p /src/oef
|
||||
# ADD . /src/oef
|
||||
|
||||
# COPY config/config.aws.yaml /src/oef/config/config.yaml
|
||||
|
||||
# WORKDIR /src/oef/
|
||||
|
||||
# RUN go build -o oef *.go
|
||||
# ENTRYPOINT ["/src/oef/oef"]
|
||||
|
||||
# EXPOSE 80
|
||||
|
|
21
dist/main.bundle.js
vendored
21
dist/main.bundle.js
vendored
File diff suppressed because one or more lines are too long
10
docker/oef_aws/db.env
Normal file
10
docker/oef_aws/db.env
Normal file
|
@ -0,0 +1,10 @@
|
|||
MYSQL_ROOT_PASSWORD=oef
|
||||
MYSQL_PASSWORD=oef
|
||||
MYSQL_DATABASE=oef_prod
|
||||
MYSQL_USER=oef
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
14
docker/oef_aws/docker-compose.yml
Normal file
14
docker/oef_aws/docker-compose.yml
Normal file
|
@ -0,0 +1,14 @@
|
|||
version: "3.3"
|
||||
|
||||
services:
|
||||
|
||||
app:
|
||||
image: andrea/oef:latest
|
||||
ports:
|
||||
- 80:80
|
||||
environment:
|
||||
- DB_HOST=oef-micro.cjsemjfwbreb.eu-central-1.rds.amazonaws.com
|
||||
- DB_PORT=3306
|
||||
|
||||
|
||||
|
|
@ -42,6 +42,7 @@ services:
|
|||
- "4444:4444"
|
||||
- "5900:5900"
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /dev/shm:/dev/shm
|
||||
|
||||
volumes:
|
||||
|
|
|
@ -240,14 +240,25 @@ func (model *Response) Update(db *Database, args map[string]string, w http.Respo
|
|||
}
|
||||
|
||||
// Check if the contest is still active
|
||||
if !response.(*Response).IsActive() {
|
||||
if isParticipant(r) && !response.(*Response).IsActive() {
|
||||
return nil, errors.OutOfTime
|
||||
}
|
||||
|
||||
response.(*Response).SingleResponses = make([]*SingleResponse, 0)
|
||||
|
||||
err = renderer.Decode(response, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If there aren't single responses then participant
|
||||
// response Starttime is resetted.
|
||||
if isAdministrator(r) {
|
||||
if len(response.(*Response).SingleResponses) == 0 {
|
||||
response.(*Response).StartTime = time.Time{}
|
||||
}
|
||||
}
|
||||
|
||||
WriteUpdater(r, response.(*Response))
|
||||
|
||||
_, err = SaveResponse(db, response)
|
||||
|
|
|
@ -231,6 +231,106 @@ func (t *testSuite) TestSchoolSubscription() {
|
|||
|
||||
}
|
||||
|
||||
func (t *testSuite) TestParticipantResponse() {
|
||||
login("WINBOXGUF", "GUf3wIP0DF7")
|
||||
|
||||
err := findElement("a.btn-primary").Click()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = findElement("#update-response").Click()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
correct := []string{
|
||||
"97", "129", "105", "113", "93", "89", "109", "141", "157",
|
||||
"85", "133", "153", "121", "117", "101", "149", "81", "145",
|
||||
"125", "137",
|
||||
}
|
||||
|
||||
for _, answerId := range correct {
|
||||
err = findElement("#answer_" + answerId).Click()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
err = findElement("button.btn-primary").Click()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
logout()
|
||||
|
||||
login("admin", "admin")
|
||||
|
||||
err = findElement("#responses_navbar_link").Click()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = findElement("#myInput").SendKeys("BOXILL")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = findElement("a.oef-selected-by-search").Click()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
scoreEl := findElement("#score")
|
||||
score, err := scoreEl.Text()
|
||||
|
||||
t.Nil(err)
|
||||
t.Equal("20", score)
|
||||
|
||||
err = findElement("a.btn-primary").Click()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = findElement("#reset_responses").Click()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = findElement("button.btn-primary").Click()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
logout()
|
||||
}
|
||||
|
||||
func (t *testSuite) TestContestActiveWindow() {
|
||||
login("admin", "admin")
|
||||
|
||||
err := searchAndClick("junior")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = findElement("a.btn-primary").Click()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = findElement("#contest_description").SendKeys("Gara per la categoria JUNIOR")
|
||||
|
||||
err = findElement("#contest_date").SendKeys("2020-03-04")
|
||||
err = findElement("#contest_start_time").SendKeys("14:00")
|
||||
err = findElement("#contest_end_time").SendKeys("15:00")
|
||||
err = findElement("#contest_duration").SendKeys("45")
|
||||
|
||||
err = findElement(".btn-primary").Click()
|
||||
|
||||
t.True(true)
|
||||
|
||||
logout()
|
||||
}
|
||||
|
||||
func findElement(selector string) selenium.WebElement {
|
||||
element, err := wd.FindElement(selenium.ByCSSSelector, selector)
|
||||
if err != nil {
|
||||
|
@ -436,3 +536,16 @@ func clickPrimaryButton() {
|
|||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func searchAndClick(text string) error {
|
||||
err := findElement("#myInput").SendKeys(text)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = findElement("a.oef-selected-by-search").Click()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
1
scripts/export_schools/.gitignore
vendored
Normal file
1
scripts/export_schools/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.csv
|
BIN
scripts/export_schools/export_schools
Executable file
BIN
scripts/export_schools/export_schools
Executable file
Binary file not shown.
36
scripts/export_schools/main.go
Normal file
36
scripts/export_schools/main.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
"git.andreafazzi.eu/andrea/oef/client"
|
||||
"git.andreafazzi.eu/andrea/oef/orm"
|
||||
"github.com/gocarina/gocsv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
username := flag.String("username", "admin", "Username")
|
||||
password := flag.String("password", "admin", "Password")
|
||||
output := flag.String("output", "schools.csv", "Output filename")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
client, err := client.Dial(flag.Arg(0), *username, *password)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
schools := make([]*orm.School, 0)
|
||||
err = client.ReadAll(&schools)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
f, err := os.Create(*output)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
gocsv.MarshalFile(schools, f)
|
||||
}
|
BIN
scripts/export_schools/scuole.xlsx
Normal file
BIN
scripts/export_schools/scuole.xlsx
Normal file
Binary file not shown.
12
src/index.js
12
src/index.js
|
@ -10,7 +10,13 @@ $(function () {
|
|||
} else {
|
||||
timeleft = 0;
|
||||
}
|
||||
$("#timeleft").html(timeleft)
|
||||
|
||||
$("#timeleft").html(timeleft);
|
||||
|
||||
var minutes = Math.floor(timeleft/60);
|
||||
var seconds = timeleft % 60;
|
||||
|
||||
$("#timeleft_view").html(minutes+":"+seconds.toString().padStart(2, "0"));
|
||||
}, 1000);
|
||||
|
||||
$("#reloadCaptcha").on("click",function(eventObject) {
|
||||
|
@ -66,4 +72,8 @@ $(function () {
|
|||
|
||||
$(".karmen-ajax-delete").on("click", deleteHandler);
|
||||
|
||||
$("#reset_responses").on("click", function(eventObject) {
|
||||
$('.form-check-input').removeAttr('checked');
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
<a class="nav-item nav-link {{.Options|active "Answer"}}" href="{{all "Answer"}}">Risposte</a>
|
||||
<a id="schools_navbar_link" class="nav-item nav-link {{.Options|active "School"}}" href="{{all "School"}}">Scuole</a>
|
||||
<a id="participants_navbar_link" class="nav-item nav-link {{.Options|active "Participant"}}" href="{{all "Participant"}}">Partecipanti</a>
|
||||
<a class="nav-item nav-link {{.Options|active "Response"}}" href="{{all "Response"}}">Prove</a>
|
||||
<a id="responses_navbar_link" class="nav-item nav-link {{.Options|active "Response"}}" href="{{all "Response"}}">Prove</a>
|
||||
{{- end -}}
|
||||
{{- if $isSchool -}}
|
||||
<a id="school_navbar_link" class="nav-item nav-link {{.Options|active "School"}}" href="{{.Claims|modelId|show "School"}}">Scuola</a>
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
<footer class="footer text-center">
|
||||
<div class="container">
|
||||
<span class="text-center text-muted">© 2020 <a href="https://www.olimpiadi-economiaefinanza.it">Olimpiadi di Economia e Finanza 2020</a> ({{version}})
|
||||
<span class="text-center text-muted">© 2020 <a href="https://www.olimpiadi-economiaefinanza.it">Olimpiadi di Economia e Finanza</a> ({{version}})
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
{{if isResponseIn $answer.ID $.Data.AnswersIDs}}
|
||||
{{$checked = true}}
|
||||
{{end}}
|
||||
<input class="form-check-input" type="radio" name="SingleResponses.{{$id}}" id="answer_{{$answer.ID}}" value="{{$answer.ID}}" required {{if $checked}}checked{{end}}>
|
||||
<input class="form-check-input" type="radio" name="SingleResponses.{{$id}}" id="answer_{{$answer.ID}}" value="{{$answer.ID}}" {{if $isParticipant}}required{{end}} {{if $checked}}checked{{end}}>
|
||||
<label class="form-check-label {{if and $isAdmin $answer.Correct}}text-success{{end}}" for="answer_{{$answer.ID}}">
|
||||
{{$answer}}
|
||||
</label>
|
||||
|
@ -71,10 +71,12 @@
|
|||
<dt>Gara</dt><dd>{{.Data.Contest}}</dd>
|
||||
<dt>Partecipante</dt><dd>{{.Data.Participant}}</dd>
|
||||
{{- if $isParticipant -}}
|
||||
<dt>Tempo rimanente</dt>{{if .Data.TimeLeft}}<dd><span id="timeleft">{{.Data.TimeLeft.Seconds|toInt}}</span> secondi rimanenti</dd>{{else}}<dd>La gara è sempre attiva</dd>{{end}}
|
||||
<span id="timeleft" style="display: none">{{.Data.TimeLeft.Seconds|toInt}}</span>
|
||||
<dt>Tempo rimanente</dt>{{if .Data.TimeLeft}}<dd><span id="timeleft_view"></span> minuti rimanenti</dd>{{else}}<dd>La gara è sempre attiva</dd>{{end}}
|
||||
{{- end -}}
|
||||
</dl>
|
||||
<button type="submit" class="btn btn-primary" form="{{$form}}">Salva</button>
|
||||
{{if $isAdmin}}<button id="reset_responses" class="btn btn-outline-secondary">Cancella risposte</button>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
<dd class="col-sm-9">nessuna risposta fornita</dd>
|
||||
{{end}}
|
||||
<dt class="col-sm-3">Punteggio</dt>
|
||||
<dd class="col-sm-9">{{.Data.Score}}</dd>
|
||||
<dd id="score" class="col-sm-9">{{.Data.Score}}</dd>
|
||||
{{if $creatorUser:=.Data.CreatedBy}}
|
||||
<dt class="col-sm-3">Creato da</dt>
|
||||
<dd class="col-sm-9">{{$creatorUser.Username}}[{{$creatorUser.Role}}] {{$.Data.CreatedAt|prettyDateTime}} da {{.Data.CreatorIP}}</dd>
|
||||
|
@ -76,7 +76,7 @@
|
|||
{{if .Data.IsActive}}
|
||||
<p>
|
||||
Per iniziare o riprendere la gara clicca sul pulsante a destra.
|
||||
<a id="new_participant_subscribe" href="{{.Data.ID | update "Response"}}" class="float-right btn btn-primary btn-sm">
|
||||
<a id="update-response" href="{{.Data.ID | update "Response"}}" class="float-right btn btn-primary btn-sm">
|
||||
<span class="fa fa-edit" aria-hidden="true"></span>
|
||||
Vai alla gara!
|
||||
</a>
|
||||
|
|
Loading…
Reference in a new issue