# Project export: TrueReviews

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 10.0
- Tagline: TrueReviews uses Hume AI to create movie reviews and ratings based on viewers reactions and emotions. Postgresql db is utilized to store movie reviews. Info used for better movie suggest algorithms.
- Devpost: https://devpost.com/software/truereviews
- GitHub: https://github.com/pormonto/True-Review
- Team: 2 GitHub contributor(s) — Porfirio Montoya (5 commits), Jet Lin (1 commits)

## Devpost submission (written by the team)

### Inspiration

We were inspired by the movie theater and the hume ai table. seeing the emotion scores updating in real time instantly lit a fire to explore the new technology.

### What it does

TrueReviews uses hume to analyze peoples reactions when watching movies and based on the emotions and reactions we create a movie review with a rating. this is then stored with postgres and sql. The reviews are then used to give people movie recommendations

### How we built it

we take videos of reactions to movies and split into 15 second segments. Send this to hume and get the reactions and emotions from the viewer and create a top three emotions list. Then using postgresql db we store these reactions. This is then used to create a movie rating system and movie recommendations.

### Challenges we ran into

Some issues we ran into is the wifi was slow and so my api requests with hume took too long and the timeout of 300ms according to some code on hume's github would stop my job before the predictions can be created. wifi made it so that queries were not able to download 10,000 videos.

### Accomplishments we're proud of

attended a lot of the workshops and learned a lot of new skills

### What we learned

We need to commit to making a project right away and move swiftly with brainstorming, decision making, and execution.

### What's next

create more features and connect hume to postgresql db to the user.

## README (from the GitHub repository)

# True-Review
System to give recommendations for content consumption based off of content consumed and emotional response to that content.

Project started on 10/28/2023 in CalHacks.
Outline for MVP:
- Collect data of movies that have existed
- Make automatic download query for video's of people's reactions to content consumed
- Query Hume AI to return JSON that will be processed into a recommendation for users of True Review


## Detected evidence (automated analysis)

Indexed codebase: 11 recognized source files, 11 KB.
- Python (language) — detected in the code
- PostgreSQL (technology) — claimed on Devpost, not found in the code
- SQL (language) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (14 of 14)

```
bashscript.sh
humemoviestream.py
humerecipefacialexpressionstest.py
humetest.py
humetextstream.py
humetopemotionspython.py
LICENSE
MyMovieDatabase.db
notebookfc9b33a577.ipynb
pythonsdkbatchhume.py
README.md
topthreeemoregex.py
topthreeemotions.py
topthreehumeimplementation.py
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- Add files via upload
- Hume ai
- Add files via upload
- Add files via upload
- Update README.md
- Initial commit

## Key source files (fetched from GitHub, selected and truncated for size)

### bashscript.sh

```shell
#!/bin/bash
NUMOFKEYS=$(wc -l < ~/Programs/list.csv)
let NUMOFKEYS--
echo $NUMOFKEYS
while IFS=, read -r field1 field2
do
	if [[ "$field2" == "primaryTitle" ]]; then
		continue
	fi
	echo "$field1 and $field2"
	yt-dlp ytsearch5:"$field2"
done < ~/Programs/list.csv
echo $field2
#yt-dlp ytsearch5:

```

### humerecipefacialexpressionstest.py

```python
import asyncio

from hume import HumeStreamClient, StreamSocket
from hume.models.config import FaceConfig


async def main():
    client = HumeStreamClient("y7wUAPHS6ihjAyZsZwp4AN8EZtTFtyIfGnKJR4nRwhwLprDa")
    config = FaceConfig(identify_faces=True)
    async with client.connect([config]) as socket:
        result = await socket.send_file("./mossman.JPG")
        print(result)

asyncio.run(main())
```

### humetextstream.py

```python
import asyncio

from hume import HumeStreamClient
from hume.models.config import LanguageConfig

samples = [
    "Mary had a little lamb,",
    "Its fleece was white as snow."
    "Everywhere the child went,"
    "The little lamb was sure to go."
]

async def main():
    client = HumeStreamClient("y7wUAPHS6ihjAyZsZwp4AN8EZtTFtyIfGnKJR4nRwhwLprDa")
    config = LanguageConfig()
    async with client.connect([config]) as socket:
        for sample in samples:
            result = await socket.send_text(sample)
            emotions = result["language"]["predictions"][0]["emotions"]
            print(emotions)

asyncio.run(main())
```

### topthreeemoregex.py

```python

import sys
import os
import pickle
import re

#=== Input File handling area ...
print("Which prediction.json to find top three: ")
input_file = input()

with open(f"{input_file}.json", 'r') as fileNow:
    rawjson = fileNow.read()

#=== Output File handling area ...

new_file = open("result_02.txt", "w")




regex1 = r"\"name\":\s*\"(\S+)\","
regex2 = r"\"score\":\s*(\d+\.\d+)}"

names = re.findall(regex1, rawjson)
scores = re.findall(regex2, rawjson)

print("names: ", names)
print("scores: ", scores)

with open("result_02.txt", "w") as new_file:
    for name, score in zip(names, scores):
        tempStr = f"{name}: {score}\n"
        new_file.write(tempStr)

    
new_file.close()
```

### humetest.py

```python
from hume import HumeBatchClient
from hume.models.config import FaceConfig
from pprint import pprint


# Provide the absolute path to the image file
image_path = "/Users/jetlin/Desktop/CalHacks/humeai/mossman.JPG"

# Use the absolute path in the urls list


client = HumeBatchClient("y7wUAPHS6ihjAyZsZwp4AN8EZtTFtyIfGnKJR4nRwhwLprDa")
# urls = [image_path]
urls = ["	https://iep.utm.edu/wp-content/media/Nietzsche.jpg"]
config = FaceConfig()
job = client.submit_job(urls, [config])

status = job.get_status()
print(f"Job status: {status}")

details = job.get_details()
run_time_ms = details.get_run_time_ms()
print(f"Job ran for {run_time_ms} milliseconds")




predictions = job.get_predictions()
pprint(predictions)
```

### topthreeemotions.py

```python
import json

print("Which prediction.json to find top three: ")
result_file = input()

# Open the JSON file and read its contents
with open(f"{result_file}.json", 'r') as fileNow:
    json_response = fileNow.read()

# Parse the JSON response
data = json.loads(json_response)

# Extract emotions and their scores
emotions_data = data[0]["results"]["predictions"][0]["models"]["face"]["grouped_predictions"][0]["predictions"][0]["emotions"]

# Sort emotions by score in descending order
sorted_emotions = sorted(emotions_data, key=lambda x: x["score"], reverse=True)

# Extract top 3 emotions
top_3_emotions = sorted_emotions[:3]

# Print the top 3 emotions
for emotion in top_3_emotions:
    print(f"{emotion['name']}: {emotion['score']}")

```

### pythonsdkbatchhume.py

```python
from google.cloud import storage
from hume import HumeBatchClient
from hume.models.config import FaceConfig, ProsodyConfig



client = HumeBatchClient("y7wUAPHS6ihjAyZsZwp4AN8EZtTFtyIfGnKJR4nRwhwLprDa", timeout=100)
# urls = ["https://mega.nz/file/w1Z2kaZS#wxaOtDFnCaTZ4kfixep4Nsv84quFRYjx0ffYEHyw4Z0"]
configs = [FaceConfig(identify_faces=True), ProsodyConfig()]
files = ["/Users/jetlin/Desktop/CalHacks/humeai/HumeReactionsCompressed/HumeReactions1compress.mp4"]
job = client.submit_job([], configs, files=files)

print(job)
print("Running...")

job.await_complete(timeout=300)
job.download_predictions("predictions.json")
print("Predictions downloaded to predictions.json")

job.download_artifacts("artifacts.zip")
print("Artifacts downloaded to artifacts.zip")

predictions = job.get_predictions()
print(predictions)
```

### humemoviestream.py

```python
import asyncio

from hume import HumeStreamClient
from hume.models.config import FaceConfig, ProsodyConfig

samples = [
    "/Users/jetlin/Desktop/CalHacks/humeai/HumeReactionsCompressed/HumeReactions1compress.mp4",
    "/Users/jetlin/Desktop/CalHacks/humeai/HumeReactionsCompressed/HumeReactions2compress.mp4",
    "/Users/jetlin/Desktop/CalHacks/humeai/HumeReactionsCompressed/HumeReactions3compress.mp4"
]

async def main():
    client = HumeStreamClient("y7wUAPHS6ihjAyZsZwp4AN8EZtTFtyIfGnKJR4nRwhwLprDa")
    configs = [FaceConfig(identify_faces=True), ProsodyConfig()]
    async with client.connect(configs) as socket:
        for sample in samples:
            result = await socket.send_file(sample)
            emotions = result["language"]["predictions"][0]["emotions"]
            print(emotions)

asyncio.run(main())
```

### humetopemotionspython.py

```python
from hume import HumeBatchClient
from hume.models.config import FaceConfig
from hume.models.config import ProsodyConfig
import json

client = HumeBatchClient("y7wUAPHS6ihjAyZsZwp4AN8EZtTFtyIfGnKJR4nRwhwLprDa")
files = ["/Users/jetlin/Desktop/CalHacks/humeai/HumeReactionsCompressed/HumeReactions1compress.mp4"]

face_config = FaceConfig()
prosody_config = ProsodyConfig()

job = client.submit_job([], [face_config, prosody_config], files=files)
print(job)
print("Running...")

result = job.await_complete(timeout=300)
job_predictions = client.get_job_predictions(job_id=job.id)

# The start and end time range of predictions to be processed
start_time = 0
end_time = 12

# Top n emotions
n_top_values = 5

# A threshold of what is defined as a peaked emotion
peak_threshold = .7

emotions_dict = dict()
peaked_emotions_w_score_time_dict = dict()


# This for facial expressions. This can be modified for other models
for file in job_predictions:
    for prediction in file['results']['predictions']:
        for grouped_prediction in prediction['models']['face']['grouped_predictions']:
            for grouped_prediction_prediction in grouped_prediction['predictions']:
                if grouped_prediction_prediction['time'] >= start_time and grouped_prediction_prediction['time'] <= end_time:
                    for emotion in grouped_prediction_prediction['emotions']:
                        if emotion['name'] not in emotions_dict:
                            emotions_dict[emotion['name']] = emotion['score']
                        else:
                            emotions_dict[emotion['name']] = emotions_dict[emotion['name']] + emotion['score']
                        if emotion['score'] >= peak_threshold:
                            peaked_emotions_w_score_time_dict.update({emotion['name']: (emotion['score'], grouped_prediction_prediction['time']) })

emotions_average = dict()
emotion_dict_length = len(emotions_dict)

for emotion, score in emotions_dict.items():
        emotions_average[emotion] = score / emotion_dict_length

ascend_sorted_emotion_average = sorted(emotions_average, key=emotions_average.get, reverse=True)

print ('The top {} expressed emotions are between timestamp {} and {} : '.format(n_top_values, start_time, end_time))

for i in range(0,n_top_values):
    print(ascend_sorted_emotion_average[i])

print('The emotions that peaked over ' + str(peak_threshold) + ' : ')
for peaked_emotions, score_time in peaked_emotions_w_score_time_dict.items():
    print("{} with score of {} at {}".format(peaked_emotions,score_time[0], score_time[1]))
```

### topthreehumeimplementation.py

```python
import numpy as np
from typing import List


class Stringifier:
    RANGES = [(0.26, 0.35), (0.35, 0.44), (0.44, 0.53), (0.53, 0.62), (0.62, 0.71), (0.71, 10)]
    ADVERBS = ["slightly", "somewhat", "moderately", "quite", "very", "extremely"]

    ADJECTIVES_48 = [
        "admiring", "adoring", "appreciative", "amused", "angry", "anxious", "awestruck", "uncomfortable", "bored",
        "calm", "focused", "contemplative", "confused", "contemptuous", "content", "hungry", "determined",
        "disappointed", "disgusted", "distressed", "doubtful", "euphoric", "embarrassed", "disturbed", "entranced",
        "envious", "excited", "fearful", "guilty", "horrified", "interested", "happy", "enamored", "nostalgic",
        "pained", "proud", "inspired", "relieved", "smitten", "sad", "satisfied", "desirous", "ashamed",
        "negatively surprised", "positively surprised", "sympathetic", "tired", "triumphant"
    ]

    ADJECTIVES_53 = [
        "admiring", "adoring", "appreciative", "amused", "angry", "annoyed", "anxious", "awestruck", "uncomfortable",
        "bored", "calm", "focused", "contemplative", "confused", "contemptuous", "content", "hungry", "desirous",
        "determined", "disappointed", "disapproving", "disgusted", "distressed", "doubtful", "euphoric", "embarrassed",
        "disturbed", "enthusiastic", "entranced", "envious", "excited", "fearful", "grateful", "guilty", "horrified",
        "interested", "happy", "enamored", "nostalgic", "pained", "proud", "inspired", "relieved", "smitten", "sad",
        "satisfied", "desirous", "ashamed", "negatively surprised", "positively surprised", "sympathetic", "tired",
        "triumphant"
    ]

    @classmethod
    def scores_to_text(cls, emotion_scores: List[float]) -> str:
        if len(emotion_scores) == 48:
            adjectives = cls.ADJECTIVES_48
        elif len(emotion_scores) == 53:
            adjectives = cls.ADJECTIVES_53
        else:
            raise ValueError(f"Invalid length for emotion_scores {len(emotion_scores)}")

        # Return "neutral" if no emotions rate highly
        if all(emotion_score < cls.RANGES[0][0] for emotion_score in emotion_scores):
            return "neutral"

        # Construct phrases for all emotions that rate highly enough
        phrases = [""] * len(emotion_scores)
        for range_idx, (range_min, range_max) in enumerate(cls.RANGES):
            for emotion_idx, emotion_score in enumerate(emotion_scores):
                if range_min < emotion_score < range_max:
                    phrases[emotion_idx] = f"{cls.ADVERBS[range_idx]} {adjectives[emotion_idx]}"

        # Sort phrases by score
        sorted_indices = np.argsort(emotion_scores)[::-1]
        phrases = [phrases[i] for i in sorted_indices if phrases[i] != ""]

        # If there is only one phrase that rates highly, return it
        if len(phrases) == 0:
            return phrases[0]

        # Return all phrases separated by conjunctions
        return ", ".join(phrases[:-1]) + ", and " + phrases[-1]
```