mirror of
https://github.com/soconnor0919/personal-website.git
synced 2025-12-15 16:24:44 -05:00
Citation generator added
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
abstract = {Human-robot interaction (HRI) research plays a pivotal role in shaping how robots communicate and collaborate with humans. However, conducting HRI studies, particularly those employing the Wizard-of-Oz (WoZ) technique, can be challenging. WoZ user studies can have complexities at the technical and methodological levels that may render the results irreproducible. We propose to address these challenges with HRIStudio, a novel web-based platform designed to streamline the design, execution, and analysis of WoZ experiments. HRIStudio offers an intuitive interface for experiment creation, real-time control and monitoring during experimental runs, and comprehensive data logging and playback tools for analysis and reproducibility. By lowering technical barriers, promoting collaboration, and offering methodological guidelines, HRIStudio aims to make human-centered robotics research easier, and at the same time, empower researchers to develop scientifically rigorous user studies.},
|
||||
author = {O'Connor, Sean and Perrone, L. Felipe},
|
||||
title = {HRIStudio: A Framework for Wizard-of-Oz Experiments in Human-Robot Interaction Studies (Late Breaking Report)},
|
||||
booktitle={33rd IEEE International Conference on Robot and Human Interactive Communication (RO-MAN)},
|
||||
booktitle={33rd IEEE International Conference on Robot and Human Interactive Communication},
|
||||
year = {2024}
|
||||
url = {https://soconnor.dev/publications/hristudio-lbr.pdf},
|
||||
paperUrl = {/publications/hristudio-lbr.pdf},
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
'use client';
|
||||
|
||||
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "~/components/ui/card";
|
||||
import { Badge } from "~/components/ui/badge";
|
||||
import { ArrowUpRight, FileText, Presentation } from "lucide-react";
|
||||
import { ArrowUpRight, BookOpenText, FileText, Presentation } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { parseBibtex } from "~/lib/bibtex";
|
||||
import { useEffect, useState } from "react";
|
||||
import type { Publication } from "~/lib/bibtex";
|
||||
import { Badge } from "~/components/ui/badge";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card";
|
||||
import { Skeleton } from "~/components/ui/skeleton";
|
||||
import type { Publication } from "~/lib/bibtex";
|
||||
import { parseBibtex } from "~/lib/bibtex";
|
||||
|
||||
export default function PublicationsPage() {
|
||||
const [publications, setPublications] = useState<Publication[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const tagsToStrip = ['paperUrl', 'posterUrl'];
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/publications.bib')
|
||||
@@ -23,6 +24,38 @@ export default function PublicationsPage() {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const downloadBibtex = (pub: Publication) => {
|
||||
const { title, authors, venue, year, doi, abstract, type, citationType, citationKey } = pub;
|
||||
let bibtexEntry = `@${citationType}{${citationKey},\n`;
|
||||
bibtexEntry += ` title = {${title}},\n`;
|
||||
bibtexEntry += ` author = {${authors.join(' and ')}},\n`;
|
||||
bibtexEntry += ` year = {${year}},\n`;
|
||||
if (type === 'conference' || type === 'workshop') {
|
||||
bibtexEntry += ` organization = {${venue}},\n`;
|
||||
} else if (type === 'journal') {
|
||||
bibtexEntry += ` journal = {${venue}},\n`;
|
||||
} else if (type === 'thesis') {
|
||||
bibtexEntry += ` school = {${venue}},\n`;
|
||||
}
|
||||
if (doi) {
|
||||
bibtexEntry += ` doi = {${doi}},\n`;
|
||||
}
|
||||
if (abstract) {
|
||||
bibtexEntry += ` abstract = {${abstract}},\n`;
|
||||
}
|
||||
bibtexEntry += `}\n`;
|
||||
|
||||
const blob = new Blob([bibtexEntry], { type: 'text/plain' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `refs.bib`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<section className="prose prose-zinc dark:prose-invert max-w-none">
|
||||
@@ -49,9 +82,9 @@ export default function PublicationsPage() {
|
||||
<CardHeader className="pb-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle>{pub.title}</CardTitle>
|
||||
{pub.url && (
|
||||
{pub.paperUrl && (
|
||||
<Link
|
||||
href={pub.url}
|
||||
href={pub.paperUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground hover:text-primary"
|
||||
@@ -113,6 +146,14 @@ export default function PublicationsPage() {
|
||||
</Badge>
|
||||
</Link>
|
||||
)}
|
||||
<Badge
|
||||
onClick={() => downloadBibtex(pub)}
|
||||
className="cursor-pointer capitalize"
|
||||
variant="outline"
|
||||
>
|
||||
<BookOpenText className="h-4 w-4" />
|
||||
BibTeX
|
||||
</Badge>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -8,12 +8,15 @@ export type Publication = {
|
||||
paperUrl?: string;
|
||||
posterUrl?: string;
|
||||
abstract?: string;
|
||||
citationType?: string;
|
||||
citationKey?: string;
|
||||
type: 'conference' | 'journal' | 'workshop' | 'thesis';
|
||||
};
|
||||
|
||||
type BibTeXEntry = {
|
||||
type: string;
|
||||
fields: Record<string, string>;
|
||||
citationKey: string;
|
||||
};
|
||||
|
||||
function parseAuthors(authorString: string): string[] {
|
||||
@@ -31,10 +34,11 @@ function parseAuthors(authorString: string): string[] {
|
||||
|
||||
function parseBibTeXEntry(entry: string): BibTeXEntry | null {
|
||||
// Match the entry type and content
|
||||
const typeMatch = entry.match(/^(\w+)\s*{\s*[\w\d-_]+\s*,/);
|
||||
const typeMatch = entry.match(/^(\w+)\s*{\s*([\w\d-_]+)\s*,/);
|
||||
if (!typeMatch) return null;
|
||||
|
||||
const type = typeMatch[1]!.toLowerCase();
|
||||
const citationKey = typeMatch[2];
|
||||
const content = entry.slice(typeMatch[0].length);
|
||||
|
||||
const fields: Record<string, string> = {};
|
||||
@@ -68,7 +72,7 @@ function parseBibTeXEntry(entry: string): BibTeXEntry | null {
|
||||
fields[currentField] = buffer.trim();
|
||||
}
|
||||
|
||||
return { type, fields };
|
||||
return { type, fields, citationKey: citationKey! };
|
||||
}
|
||||
|
||||
export function parseBibtex(bibtex: string): Publication[] {
|
||||
@@ -79,10 +83,21 @@ export function parseBibtex(bibtex: string): Publication[] {
|
||||
.filter((entry): entry is BibTeXEntry => entry !== null);
|
||||
|
||||
return entries.map(entry => {
|
||||
const publicationType =
|
||||
entry.type === 'inproceedings' ? 'conference' :
|
||||
entry.type === 'article' ? 'journal' :
|
||||
entry.type === 'mastersthesis' ? 'thesis' : 'workshop';
|
||||
const publicationType = (() => {
|
||||
switch (entry.type) {
|
||||
case 'inproceedings':
|
||||
case 'conference':
|
||||
return 'conference';
|
||||
case 'article':
|
||||
return 'journal';
|
||||
case 'mastersthesis':
|
||||
return 'thesis';
|
||||
case 'workshop':
|
||||
return 'workshop';
|
||||
default:
|
||||
return 'journal';
|
||||
}
|
||||
})();
|
||||
|
||||
return {
|
||||
title: entry.fields.title?.replace(/[{}]/g, '') || '',
|
||||
@@ -94,6 +109,8 @@ export function parseBibtex(bibtex: string): Publication[] {
|
||||
paperUrl: entry.fields.paperurl,
|
||||
posterUrl: entry.fields.posterurl,
|
||||
abstract: entry.fields.abstract,
|
||||
citationType: entry.type,
|
||||
citationKey: entry.citationKey,
|
||||
type: publicationType
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user