fic: Exercice can have heading.jpg

This commit is contained in:
nemunaire 2023-06-14 17:34:18 +02:00
parent f366d6b8c1
commit abe5ad61d4
5 changed files with 42 additions and 11 deletions

View File

@ -612,7 +612,7 @@ func createExercice(c *gin.Context) {
} }
} }
exercice, err := theme.AddExercice(ue.Title, ue.WIP, ue.URLId, ue.Path, ue.Statement, ue.Overview, ue.Headline, depend, ue.Gain, ue.VideoURI, ue.Resolution, ue.SeeAlso, ue.Finished) exercice, err := theme.AddExercice(ue.Title, ue.Image, ue.WIP, ue.URLId, ue.Path, ue.Statement, ue.Overview, ue.Headline, depend, ue.Gain, ue.VideoURI, ue.Resolution, ue.SeeAlso, ue.Finished)
if err != nil { if err != nil {
log.Println("Unable to createExercice:", err.Error()) log.Println("Unable to createExercice:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during exercice creation."}) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during exercice creation."})

View File

@ -1717,7 +1717,7 @@ angular.module("FICApp")
} }
}); });
$scope.exercices = Exercice.query(); $scope.exercices = Exercice.query();
$scope.fields = ["title", "urlid", "disabled", "statement", "headline", "overview", "finished", "depend", "gain", "coefficient", "videoURI", "resolution", "issue", "issuekind", "wip"]; $scope.fields = ["title", "urlid", "disabled", "statement", "headline", "overview", "finished", "depend", "gain", "coefficient", "videoURI", "image", "resolution", "issue", "issuekind", "wip"];
$scope.inSync = false; $scope.inSync = false;
$scope.syncExo = function() { $scope.syncExo = function() {

View File

@ -3,6 +3,7 @@ package sync
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"image"
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
@ -213,6 +214,14 @@ func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]*
} }
} }
if i.Exists(path.Join(epath, "heading.jpg")) {
e.Image = path.Join(epath, "heading.jpg")
} else if i.Exists(path.Join(epath, "heading.png")) {
e.Image = path.Join(epath, "heading.png")
} else if theme.Image == "" {
errs = append(errs, NewExerciceError(e, fmt.Errorf("heading.jpg: No such file")))
}
// Parse challenge.txt // Parse challenge.txt
var md toml.MetaData var md toml.MetaData
p, md, err = parseExerciceParams(i, epath) p, md, err = parseExerciceParams(i, epath)
@ -340,6 +349,25 @@ func SyncExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]*f
} }
if e != nil { if e != nil {
if len(e.Image) > 0 {
if _, err := i.importFile(e.Image,
func(filePath string, origin string) (interface{}, error) {
if err := resizePicture(filePath, image.Rect(0, 0, 500, 300)); err != nil {
return nil, err
}
e.Image = strings.TrimPrefix(filePath, fic.FilesDir)
// If the theme has no image yet, use the first exercice's image found
theme.Image = e.Image
_, err := theme.Update()
return nil, err
}); err != nil {
errs = append(errs, NewExerciceError(e, fmt.Errorf("unable to import heading image: %w", err)))
}
}
// Create or update the exercice // Create or update the exercice
err = theme.SaveNamedExercice(e) err = theme.SaveNamedExercice(e)
if err != nil { if err != nil {

View File

@ -142,6 +142,7 @@ CREATE TABLE IF NOT EXISTS exercices(
id_exercice INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_exercice INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
id_theme INTEGER NOT NULL, id_theme INTEGER NOT NULL,
title VARCHAR(255) NOT NULL, title VARCHAR(255) NOT NULL,
image VARCHAR(255) NOT NULL,
disabled BOOLEAN NOT NULL DEFAULT 0, disabled BOOLEAN NOT NULL DEFAULT 0,
headline TEXT NOT NULL, headline TEXT NOT NULL,
url_id VARCHAR(255) NOT NULL, url_id VARCHAR(255) NOT NULL,
@ -561,7 +562,7 @@ func DBRecreateDiscountedView() (err error) {
return return
} }
_, err = db.Exec("CREATE OR REPLACE VIEW exercices_discounted AS SELECT E.id_exercice, E.id_theme, E.title, E.disabled, E.headline, E.url_id, E.path, E.statement, E.overview, E.issue, E.issue_kind, E.depend, E.gain - " + fmt.Sprintf("%f", DiscountedFactor) + " * E.gain * (COUNT(*) - 1) AS gain, E.coefficient_cur, E.finished, E.video_uri, E.resolution, E.seealso FROM exercices E LEFT OUTER JOIN exercice_solved S ON S.id_exercice = E.id_exercice GROUP BY E.id_exercice;") _, err = db.Exec("CREATE OR REPLACE VIEW exercices_discounted AS SELECT E.id_exercice, E.id_theme, E.title, E.image, E.disabled, E.headline, E.url_id, E.path, E.statement, E.overview, E.issue, E.issue_kind, E.depend, E.gain - " + fmt.Sprintf("%f", DiscountedFactor) + " * E.gain * (COUNT(*) - 1) AS gain, E.coefficient_cur, E.finished, E.video_uri, E.resolution, E.seealso FROM exercices E LEFT OUTER JOIN exercice_solved S ON S.id_exercice = E.id_exercice GROUP BY E.id_exercice;")
return return
} }

View File

@ -25,6 +25,7 @@ type Exercice struct {
IdTheme int64 `json:"id_theme"` IdTheme int64 `json:"id_theme"`
Language string `json:"lang,omitempty"` Language string `json:"lang,omitempty"`
Title string `json:"title"` Title string `json:"title"`
Image string `json:"image"`
// Disabled indicates if the exercice is available to players now or not // Disabled indicates if the exercice is available to players now or not
Disabled bool `json:"disabled"` Disabled bool `json:"disabled"`
// WIP indicates if the exercice is in development or not // WIP indicates if the exercice is in development or not
@ -73,7 +74,7 @@ func (e *Exercice) AnalyzeTitle() {
func getExercice(table, condition string, args ...interface{}) (*Exercice, error) { func getExercice(table, condition string, args ...interface{}) (*Exercice, error) {
var e Exercice var e Exercice
var tmpgain float64 var tmpgain float64
if err := DBQueryRow("SELECT id_exercice, id_theme, title, disabled, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri, resolution, seealso, finished FROM "+table+" "+condition, args...).Scan(&e.Id, &e.IdTheme, &e.Title, &e.Disabled, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &tmpgain, &e.Coefficient, &e.VideoURI, &e.Resolution, &e.SeeAlso, &e.Finished); err != nil { if err := DBQueryRow("SELECT id_exercice, id_theme, title, image, disabled, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri, resolution, seealso, finished FROM "+table+" "+condition, args...).Scan(&e.Id, &e.IdTheme, &e.Title, &e.Image, &e.Disabled, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &tmpgain, &e.Coefficient, &e.VideoURI, &e.Resolution, &e.SeeAlso, &e.Finished); err != nil {
return nil, err return nil, err
} }
e.Gain = int64(math.Trunc(tmpgain)) e.Gain = int64(math.Trunc(tmpgain))
@ -113,7 +114,7 @@ func GetDiscountedExercice(id int64) (*Exercice, error) {
// getExercices returns the list of all challenges present in the database. // getExercices returns the list of all challenges present in the database.
func getExercices(table string) ([]*Exercice, error) { func getExercices(table string) ([]*Exercice, error) {
if rows, err := DBQuery("SELECT id_exercice, id_theme, title, disabled, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri, resolution, seealso, finished FROM " + table + " ORDER BY path ASC"); err != nil { if rows, err := DBQuery("SELECT id_exercice, id_theme, title, image, disabled, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri, resolution, seealso, finished FROM " + table + " ORDER BY path ASC"); err != nil {
return nil, err return nil, err
} else { } else {
defer rows.Close() defer rows.Close()
@ -122,7 +123,7 @@ func getExercices(table string) ([]*Exercice, error) {
for rows.Next() { for rows.Next() {
e := &Exercice{} e := &Exercice{}
var tmpgain float64 var tmpgain float64
if err := rows.Scan(&e.Id, &e.IdTheme, &e.Title, &e.Disabled, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &tmpgain, &e.Coefficient, &e.VideoURI, &e.Resolution, &e.SeeAlso, &e.Finished); err != nil { if err := rows.Scan(&e.Id, &e.IdTheme, &e.Title, &e.Image, &e.Disabled, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &tmpgain, &e.Coefficient, &e.VideoURI, &e.Resolution, &e.SeeAlso, &e.Finished); err != nil {
return nil, err return nil, err
} }
e.Gain = int64(math.Trunc(tmpgain)) e.Gain = int64(math.Trunc(tmpgain))
@ -151,7 +152,7 @@ func GetDiscountedExercices() ([]*Exercice, error) {
// GetExercices returns the list of all challenges in the Theme. // GetExercices returns the list of all challenges in the Theme.
func (t *Theme) GetExercices() ([]*Exercice, error) { func (t *Theme) GetExercices() ([]*Exercice, error) {
if rows, err := DBQuery("SELECT id_exercice, id_theme, title, disabled, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri, resolution, seealso, finished FROM exercices WHERE id_theme = ? ORDER BY path ASC", t.Id); err != nil { if rows, err := DBQuery("SELECT id_exercice, id_theme, title, image, disabled, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri, resolution, seealso, finished FROM exercices WHERE id_theme = ? ORDER BY path ASC", t.Id); err != nil {
return nil, err return nil, err
} else { } else {
defer rows.Close() defer rows.Close()
@ -159,7 +160,7 @@ func (t *Theme) GetExercices() ([]*Exercice, error) {
exos := []*Exercice{} exos := []*Exercice{}
for rows.Next() { for rows.Next() {
e := &Exercice{} e := &Exercice{}
if err := rows.Scan(&e.Id, &e.IdTheme, &e.Title, &e.Disabled, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI, &e.Resolution, &e.SeeAlso, &e.Finished); err != nil { if err := rows.Scan(&e.Id, &e.IdTheme, &e.Title, &e.Image, &e.Disabled, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI, &e.Resolution, &e.SeeAlso, &e.Finished); err != nil {
return nil, err return nil, err
} }
e.AnalyzeTitle() e.AnalyzeTitle()
@ -222,7 +223,7 @@ func (t *Theme) addExercice(e *Exercice) (err error) {
if e.WIP { if e.WIP {
wip = "%" wip = "%"
} }
if res, err := DBExec("INSERT INTO exercices (id_theme, title, disabled, url_id, path, statement, overview, finished, headline, issue, depend, gain, video_uri, resolution, seealso, issue_kind, coefficient_cur) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, "+ik+", "+cc+")", t.Id, wip+e.Title, e.Disabled, e.URLId, e.Path, e.Statement, e.Overview, e.Finished, e.Headline, e.Issue, e.Depend, e.Gain, e.VideoURI, e.Resolution, e.SeeAlso); err != nil { if res, err := DBExec("INSERT INTO exercices (id_theme, title, image, disabled, url_id, path, statement, overview, finished, headline, issue, depend, gain, video_uri, resolution, seealso, issue_kind, coefficient_cur) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, "+ik+", "+cc+")", t.Id, wip+e.Title, e.Image, e.Disabled, e.URLId, e.Path, e.Statement, e.Overview, e.Finished, e.Headline, e.Issue, e.Depend, e.Gain, e.VideoURI, e.Resolution, e.SeeAlso); err != nil {
return err return err
} else if eid, err := res.LastInsertId(); err != nil { } else if eid, err := res.LastInsertId(); err != nil {
return err return err
@ -234,7 +235,7 @@ func (t *Theme) addExercice(e *Exercice) (err error) {
} }
// AddExercice creates and fills a new struct Exercice and registers it into the database. // AddExercice creates and fills a new struct Exercice and registers it into the database.
func (t *Theme) AddExercice(title string, wip bool, urlId string, path string, statement string, overview string, headline string, depend *Exercice, gain int64, videoURI string, resolution string, seealso string, finished string) (e *Exercice, err error) { func (t *Theme) AddExercice(title string, image string, wip bool, urlId string, path string, statement string, overview string, headline string, depend *Exercice, gain int64, videoURI string, resolution string, seealso string, finished string) (e *Exercice, err error) {
var dpd *int64 = nil var dpd *int64 = nil
if depend != nil { if depend != nil {
dpd = &depend.Id dpd = &depend.Id
@ -242,6 +243,7 @@ func (t *Theme) AddExercice(title string, wip bool, urlId string, path string, s
e = &Exercice{ e = &Exercice{
Title: title, Title: title,
Image: image,
Disabled: false, Disabled: false,
WIP: wip, WIP: wip,
URLId: urlId, URLId: urlId,
@ -269,7 +271,7 @@ func (e *Exercice) Update() (int64, error) {
wip = "%" wip = "%"
} }
if res, err := DBExec("UPDATE exercices SET title = ?, disabled = ?, url_id = ?, path = ?, statement = ?, overview = ?, headline = ?, issue = ?, issue_kind = ?, depend = ?, gain = ?, coefficient_cur = ?, video_uri = ?, resolution = ?, seealso = ?, finished = ? WHERE id_exercice = ?", wip+e.Title, e.Disabled, e.URLId, e.Path, e.Statement, e.Overview, e.Headline, e.Issue, e.IssueKind, e.Depend, e.Gain, e.Coefficient, e.VideoURI, e.Resolution, e.SeeAlso, e.Finished, e.Id); err != nil { if res, err := DBExec("UPDATE exercices SET title = ?, image = ?, disabled = ?, url_id = ?, path = ?, statement = ?, overview = ?, headline = ?, issue = ?, issue_kind = ?, depend = ?, gain = ?, coefficient_cur = ?, video_uri = ?, resolution = ?, seealso = ?, finished = ? WHERE id_exercice = ?", wip+e.Title, e.Image, e.Disabled, e.URLId, e.Path, e.Statement, e.Overview, e.Headline, e.Issue, e.IssueKind, e.Depend, e.Gain, e.Coefficient, e.VideoURI, e.Resolution, e.SeeAlso, e.Finished, e.Id); err != nil {
return 0, err return 0, err
} else if nb, err := res.RowsAffected(); err != nil { } else if nb, err := res.RowsAffected(); err != nil {
return 0, err return 0, err