The awards management system enables administrators to configure festival awards, assign them to winning films, and organize films into categories.
Award Data Model
Awards are simple entities that link to films:
// /back/src/models/Award.js:5-28
const Award = sequelize . define ( 'Award' , {
id_award: {
type: DataTypes . INTEGER ,
primaryKey: true ,
autoIncrement: true
},
id_movie: {
type: DataTypes . INTEGER ,
allowNull: false
},
award_name: DataTypes . STRING ( 100 )
}, {
tableName: 'awards' ,
timestamps: true
});
Award . associate = function ( models ) {
Award . belongsTo ( models . Movie , {
foreignKey: 'id_movie'
});
};
Each award is linked to a specific film. A film can receive multiple awards (e.g., “Best Film”, “Audience Choice”, “Technical Excellence”).
Award Types
Common festival award categories:
Major Awards
Technical Awards
Special Awards
Primary Festival Recognitions
Best Film - Overall top film selection
Grand Prix - Jury’s highest honor
Best Director - Outstanding direction
Best Screenplay - Exceptional writing
Best Actor/Actress - Performance awards
These awards typically require candidate status and jury consensus. Craft & Technique Excellence
Best Cinematography - Visual excellence
Best Editing - Superior post-production
Best Sound Design - Audio innovation
Best Visual Effects - Technical achievement
Best AI Integration - Novel AI usage (MarsAI specific)
Technical awards recognize specific production elements. Additional Recognitions
Audience Choice - Popular vote winner
Jury Special Mention - Noteworthy recognition
Innovation Award - Creative breakthrough
Emerging Talent - New filmmaker recognition
Festival Director’s Choice - Curator selection
Special awards may bypass standard voting process.
Creating Awards
Admin Award Creation
Administrators create awards and assign them to films:
// POST /awards/:id_movie
async function createAward ( req , res ) {
const { award_name } = req . body ;
const { id_movie } = req . params ;
if ( ! award_name ) {
return res . status ( 400 ). json ({
error: "Tous les champs doivent être remplis"
});
}
// Check for duplicate
const existingAward = await Award . findOne ({
where: { award_name }
});
if ( existingAward ) {
return res . json ({
message: "Award déjà existant" ,
award: existingAward
});
}
// Create new award
const newAward = await Award . create ({
award_name: award_name ,
id_movie: id_movie
});
return res . status ( 201 ). json ({
message: "Award créé" ,
newAward
});
}
Bulk Award Assignment
// Example: Assign multiple awards
const awards = [
{ award_name: "Best Film" , id_movie: 42 },
{ award_name: "Best Director" , id_movie: 42 },
{ award_name: "Technical Excellence" , id_movie: 42 }
];
for ( const award of awards ) {
await Award . create ( award );
}
// Update film status
await Movie . update (
{ selection_status: "awarded" },
{ where: { id_movie: 42 } }
);
When a film receives its first award, its selection_status should be updated to “awarded” to reflect its winning status.
Retrieving Awards
Get All Awards
// GET /awards
function getAward ( req , res ) {
Award . findAll (). then (( awards ) => {
res . json ( awards );
});
}
Get Award by ID
// GET /awards/:id
function getAwardById ( req , res ) {
const { id } = req . params ;
Award . findOne ({ where: { id_award: id } })
. then (( award ) => {
if ( award ) {
res . json ( award );
} else {
res . status ( 404 ). json ({ error: "Award non trouvé" });
}
});
}
Get Awards for Film
// Get film with all its awards
const movie = await Movie . findByPk ( id_movie , {
include: [{
model: Award ,
required: false
}]
});
// Response format:
{
"id_movie" : 42 ,
"title" : "AI Dreams" ,
"selection_status" : "awarded" ,
"Awards" : [
{
"id_award" : 1 ,
"award_name" : "Best Film" ,
"createdAt" : "2024-03-15T10:00:00Z"
},
{
"id_award" : 2 ,
"award_name" : "Best Director" ,
"createdAt" : "2024-03-15T10:05:00Z"
}
]
}
Updating Awards
Modify Award Details
// PUT /awards/:id
function updateAward ( req , res ) {
const { id } = req . params ;
const { award_name , id_movie } = req . body ;
Award . findOne ({ where: { id_award: id } })
. then (( award ) => {
if ( ! award ) {
return res . status ( 404 ). json ({
error: "Award non trouvé"
});
}
// Update fields
if ( award_name ) {
award . award_name = award_name ;
}
if ( id_movie ) {
award . setDataValue ( "id_movie" , Number ( id_movie ));
award . changed ( "id_movie" , true );
}
return award . save ();
})
. then (( updatedAward ) => {
res . json ({
message: "Award mis à jour" ,
updatedAward
});
})
. catch (( err ) => {
res . status ( 500 ). json ({ error: err . message });
});
}
Reassign Award to Different Film
// Transfer award from one film to another
const award = await Award . findByPk ( id_award );
await award . update ({ id_movie: new_movie_id });
Deleting Awards
// DELETE /awards/:id
function deleteAward ( req , res ) {
const { id } = req . params ;
Award . destroy ({ where: { id_award: id } })
. then (() => {
return res . status ( 200 ). json ({
message: "Prix supprimé"
});
});
}
Deleting an award does not automatically change the film’s status. If a film has no remaining awards, manually update its status from “awarded” to “candidate”.
Category Management
Category Model
Categories organize films by type:
// /back/src/models/Categorie.js:5-26
const Categorie = sequelize . define ( 'Categorie' , {
id_categorie: {
type: DataTypes . INTEGER ,
primaryKey: true ,
autoIncrement: true
},
name: DataTypes . STRING
}, {
tableName: 'categories' ,
timestamps: true
});
Categorie . associate = function ( models ) {
Categorie . belongsToMany ( models . Movie , {
through: 'movies_categories' ,
foreignKey: 'id_categorie' ,
otherKey: 'id_movie'
});
};
Many-to-Many Relationship
Films can belong to multiple categories:
// /back/src/models/Movie.js:134-138
Movie . belongsToMany ( models . Categorie , {
through: 'movies_categories' ,
foreignKey: 'id_movie' ,
otherKey: 'id_categorie'
});
Example Categories
Animation - Animated films
Documentary - Non-fiction films
Narrative - Story-driven fiction
Experimental - Avant-garde works
AI-Generated - Fully AI-created content
AI-Assisted - Hybrid human-AI production
Student Films - Educational projects
International - Non-local productions
Assigning Categories to Films
Single Film Category Update
// PUT /movies/:id/categories (ADMIN only)
async function updateMovieCategories ( req , res ) {
const { id } = req . params ;
let { categories } = req . body ;
// Parse if JSON string
if ( typeof categories === "string" ) {
try {
categories = JSON . parse ( categories );
} catch ( parseError ) {
categories = [];
}
}
// Ensure array of integers
const categoryIds = categories
. map (( value ) => Number ( value ))
. filter (( value ) => Number . isInteger ( value ));
const movie = await Movie . findByPk ( id );
if ( ! movie ) {
return res . status ( 404 ). json ({ error: "Film non trouvé" });
}
// Set categories (replaces existing)
await movie . setCategories ( categoryIds );
// Fetch updated movie with categories
const updatedMovie = await Movie . findByPk ( id , {
include: [
{ model: Categorie , through: { attributes: [] } }
]
});
res . json ({
message: "Catégories mises à jour" ,
movie: updatedMovie
});
}
Category Assignment During Submission
// During film creation
const newMovie = await Movie . create ({
title: "AI Dreams" ,
description: "A film about..." ,
id_user: userId
});
// Assign to categories
const categoryIds = [ 1 , 3 , 5 ]; // Animation, AI-Generated, International
await newMovie . setCategories ( categoryIds );
Award Workflow Integration
Step 1: Jury Voting Complete
Films reach “candidate” status after positive jury consensus.
const candidates = await Movie . findAll ({
where: { selection_status: "candidate" },
include: [
{ model: Vote },
{ model: User , as: "Producer" }
]
});
Administrators review candidates and determine award recipients.
// Review vote consensus
const votes = await Vote . findAll ({
where: { id_movie: candidateId }
});
const yesVotes = votes . filter ( v => v . note === "YES" ). length ;
const consensus = yesVotes / votes . length ;
if ( consensus >= 0.75 ) {
// High consensus - strong candidate
}
Create awards for winning films.
// Assign Best Film award
const award = await Award . create ({
award_name: "Best Film" ,
id_movie: winningFilmId
});
// Update film status
const movie = await Movie . findByPk ( winningFilmId );
movie . selection_status = "awarded" ;
await movie . save ();
Notify producers of their awards.
import { sendTemplateEmail } from "./utils/mailer.js" ;
const movie = await Movie . findByPk ( winningFilmId , {
include: [{ model: User , as: "Producer" }]
});
await sendTemplateEmail ({
to: movie . Producer . email ,
templateId: process . env . BREVO_TEMPLATE_AWARD_NOTIFICATION ,
params: {
first_name: movie . Producer . first_name ,
movie_title: movie . title ,
award_name: "Best Film"
}
});
Dashboard Views
Admin Award Management
// Get all awarded films with their awards
const awardedFilms = await Movie . findAll ({
where: { selection_status: "awarded" },
include: [
{
model: Award ,
required: true
},
{
model: User ,
as: "Producer" ,
attributes: [ "first_name" , "last_name" , "email" ]
}
],
order: [[ Award , "createdAt" , "DESC" ]]
});
Public Award Display
// Get winners for public website
const winners = await Award . findAll ({
include: [{
model: Movie ,
include: [
{ model: User , as: "Producer" },
{ model: Categorie }
]
}],
order: [[ "createdAt" , "DESC" ]]
});
// Group by award type
const groupedAwards = winners . reduce (( acc , award ) => {
if ( ! acc [ award . award_name ]) {
acc [ award . award_name ] = [];
}
acc [ award . award_name ]. push ( award . Movie );
return acc ;
}, {});
API Endpoints Summary
Method Endpoint Role Description GET /awardsALL Get all awards GET /awards/:idALL Get award by ID POST /awards/:id_movieADMIN Create award for film PUT /awards/:idADMIN Update award DELETE /awards/:idADMIN Delete award PUT /movies/:id/categoriesADMIN Assign categories to film GET /categoriesALL Get all categories POST /categoriesADMIN Create new category
Frontend Integration Example
Award Assignment Interface
import { useMutation , useQuery } from "@tanstack/react-query" ;
function AwardAssignment ({ movieId }) {
const [ awardName , setAwardName ] = useState ( "" );
// Fetch existing awards
const { data : movie } = useQuery ({
queryKey: [ "movie" , movieId ],
queryFn : () => fetchMovie ( movieId )
});
// Create award mutation
const createAwardMutation = useMutation ({
mutationFn : ( data ) => createAward ( movieId , data ),
onSuccess : () => {
alert ( "Prix attribué avec succès" );
setAwardName ( "" );
}
});
const handleSubmit = ( e ) => {
e . preventDefault ();
createAwardMutation . mutate ({ award_name: awardName });
};
return (
< div >
< h2 > { movie ?. title } </ h2 >
< h3 > Prix attribués: </ h3 >
< ul >
{ movie ?. Awards ?. map ( award => (
< li key = { award . id_award } > { award . award_name } </ li >
)) }
</ ul >
< form onSubmit = { handleSubmit } >
< input
type = "text"
value = { awardName }
onChange = { ( e ) => setAwardName ( e . target . value ) }
placeholder = "Nom du prix"
required
/>
< button type = "submit" > Attribuer le prix </ button >
</ form >
</ div >
);
}
Category Filter
function CategoryFilter () {
const [ selectedCategory , setSelectedCategory ] = useState ( null );
const { data : categories } = useQuery ({
queryKey: [ "categories" ],
queryFn: fetchCategories
});
const { data : films } = useQuery ({
queryKey: [ "films" , selectedCategory ],
queryFn : () => fetchFilmsByCategory ( selectedCategory )
});
return (
< div >
< select onChange = { ( e ) => setSelectedCategory ( e . target . value ) } >
< option value = "" > Toutes les catégories </ option >
{ categories ?. map ( cat => (
< option key = { cat . id_categorie } value = { cat . id_categorie } >
{ cat . name }
</ option >
)) }
</ select >
< div >
{ films ?. map ( film => (
< FilmCard key = { film . id_movie } film = { film } />
)) }
</ div >
</ div >
);
}
Best Practices
Use clear, descriptive names
Follow festival tradition (e.g., “Grand Prix” vs “Best Film”)
Be consistent across years
Consider bilingual names for international festivals
Avoid overly long names (max 100 characters)
Create mutually exclusive categories when possible
Allow multiple categories per film for flexibility
Use categories to organize jury assignments
Consider category-specific awards
Review and update categories annually
Verify film is in “candidate” status
Review jury consensus before awarding
Document decision rationale
Update film status to “awarded”
Send notification to producer immediately
Prepare public announcement materials
Statistics & Reporting
Award Distribution
// Count awards per category
const awardStats = await Award . findAll ({
attributes: [
'award_name' ,
[ sequelize . fn ( 'COUNT' , sequelize . col ( 'id_award' )), 'count' ]
],
group: [ 'award_name' ]
});
Category Coverage
// Films per category
const categoryStats = await Categorie . findAll ({
attributes: [
'name' ,
[ sequelize . fn ( 'COUNT' , sequelize . col ( 'Movies.id_movie' )), 'film_count' ]
],
include: [{
model: Movie ,
attributes: [],
through: { attributes: [] }
}],
group: [ 'Categorie.id_categorie' ]
});
Export Award Data
// Generate award ceremony program
async function generateAwardProgram () {
const awards = await Award . findAll ({
include: [{
model: Movie ,
include: [
{ model: User , as: "Producer" },
{ model: Categorie }
]
}],
order: [[ "award_name" , "ASC" ]]
});
return awards . map ( award => ({
award: award . award_name ,
film: award . Movie . title ,
director: ` ${ award . Movie . Producer . first_name } ${ award . Movie . Producer . last_name } ` ,
categories: award . Movie . Categories . map ( c => c . name ),
duration: award . Movie . duration
}));
}